#pragma once #include #include "ProgressBar.h" #include #include template void runParallel( std::function processElement, TCollection& collection, int maxThreadCount) { using future_type = std::future; std::mutex mutex; int currentThreadCount = 0; std::condition_variable elementFinished; future_type finishedElement; // Before exiting, wait for all running tasks to finish, but don't re-throw exceptions. // This only applies if one task already failed with an exception. auto finishRunning = gsl::finally([&]{ std::unique_lock lock(mutex); elementFinished.wait(lock, [&] { return currentThreadCount == 0; }); }); // Asyncronously run all elements for (auto it = collection.begin(); it != collection.end(); ++it) { // This variable will later hold the future, but can be value-captured right now auto future = std::make_shared(); // Notifies that an element is done processing auto notifyElementDone = [&, future] { std::lock_guard lock(mutex); finishedElement = std::move(*future); --currentThreadCount; elementFinished.notify_one(); }; // Processes the current element, then notifies auto wrapperFunction = [processElement, &element = *it, notifyElementDone]() { auto done = gsl::finally(notifyElementDone); processElement(element); }; // Asynchronously process element { std::lock_guard lock(mutex); *future = std::async(std::launch::async, wrapperFunction); ++currentThreadCount; } // Wait for threads to finish, if necessary { std::unique_lock lock(mutex); int targetThreadCount = it == collection.end() ? 0 : maxThreadCount - 1; while (currentThreadCount > targetThreadCount) { elementFinished.wait(lock); if (finishedElement.valid()) { // Re-throw any exception finishedElement.get(); finishedElement = future_type(); } } } } } template void runParallel( std::function processElement, TCollection& collection, int maxThreadCount, ProgressSink& progressSink, std::function getElementProgressWeight = [](typename TCollection::reference) { return 1.0; }) { // Create a collection of wrapper functions that take care of progress handling ProgressMerger progressMerger(progressSink); std::vector> functions; for (auto& element : collection) { auto& elementProgressSink = progressMerger.addSink(getElementProgressWeight(element)); functions.push_back([&]() { processElement(element, elementProgressSink); }); } // Run wrapper function runParallel([&](std::function function) { function(); }, functions, maxThreadCount); } inline int getProcessorCoreCount() { int coreCount = std::thread::hardware_concurrency(); // If the number of cores cannot be determined, use a reasonable default return coreCount != 0 ? coreCount : 4; }