This library aims to provide some functionality related with concurrent programming missing in C++14 with strong focus on thread pools.
What it provides:
- task queue which uses fixed size thread pool
- task queue which uses dynamically sized thread pool
- synchronization barrier
- semaphore
gcc 4.9 | gcc 5 | gcc 6 | gcc 7 |
---|---|---|---|
clang 3.9 | clang 4 | clang 5 |
---|---|---|
The library contains of multiple header files. Add contents of src
to your include path.
The classes are as generic as possible, so in order to avoid long type
declarations, file task_queues.hpp
provides most useful aliases.
N-threaded task queues types:
n_threaded_fifo_task_queue
n_threaded_lifo_task_queue
n_threaded_priority_task_queue
Dynamic task queues types:
dynamic_fifo_task_queue
dynamic_lifo_task_queue
dynamic_priority_task_queue
Using fifo and lifo queues:
#include <task_queues.hpp>
#include <iostream>
int main() {
// Declare a queue which uses 4-threaded thread pool
concurrent::n_threaded_fifo_task_queue queue(4);
// Add task to queue using push method.
// You can push everything convertible
// to std::function<void(void)>.
queue.push([] { std::cout << "Hello from task queue!"; });
// Don't worry about joining threads and task completion -
// all tasks are guaranteed to be finished after destruction of
// queue.
}
In some scenarios you may want to wait explicitly for task completion
but avoid destroying task queue. For this use wait_for_tasks_completion
method.
#include <task_queues.hpp>
#include <iostream>
int main() {
concurrent::n_threaded_fifo_task_queue queue(4);
// Add tasks to queue.
queue.push([] { /* a task */ });
queue.wait_for_tasks_completion();
// Here you can be sure that all previously pushed tasks are
// finished.
}
Getting a return value from task is also possible. The std::future
class is used to represent the result of future computations.
#include <task_queues.hpp>
#include <iostream>
int main() {
concurrent::n_threaded_fifo_task_queue queue(4);
// Add tasks to queue and wait for result.
std::future<int> future_result = queue.push_with_result([] { return 4; });
// Blocks until task is completed.
int result = future_result.get();
// Prints 4.
std::cout << result << std::endl;
}
Usage of priority task queue is very similar to usage of other types of queues but all tasks are required to have priority. Tasks with greater priority are taken from queue by worker threads before tasks with lesser priority.
#include <task_queues.hpp>
#include <iostream>
int main() {
concurrent::n_threaded_priority_task_queue queue(4);
// Use emplace method to add task with priority to queue.
task_queue.emplace(
10, // Priority is 10.
[] {
// Do somehting
}
);
// You can also push a pair of priority and task.
task_queue.push(std::make_pair(0, []{/* Do something. */}));
}
#include <task_queues.hpp>
#include <parallel_for_each.hpp>
#include <iostream>
int main() {
std::vector<double> values = {1, 2, 3, 4, 5, 6, 7, 8};
concurrent::n_threaded_fifo_task_queue task_queue(4);
concurrent::parallel_for_each(
task_queue,
values.begin(),
values.end(),
[] (double &value) {
value *= value;
}
);
// Expected output: 1 4 9 16 25 36 49 64
for (auto value: values) {
std::cout << value << " ";
}
std::cout << std::endl;
}