Repository files navigation

testscodecovFile size in bytesLicenseLicenseLicense: MIT

task-thread-pool is a fast and lightweight thread pool for C++11 and newer.

Easily add parallelism to your project without introducing heavy dependencies.

  • Focus on correctness, ease of use, simplicity, performance.
  • Small single header file and permissive licensing means easy integration.
  • Tested on all major platforms and compilers:
    • Linux, macOS, Windows
    • GCC, LLVM/Clang, MSVC, MinGW, Emscripten
      • CI tests on GCC 7+ and LLVM 7+, should work on older
    • C++11, C++14, C++17, C++20, C++23
  • Comprehensive test suite, including stress tests.
  • Benchmarks help confirm good performance.

Usage

#include<task_thread_pool.hpp>
task_thread_pool::task_thread_pool pool; // num_threads = number of physical cores// or
task_thread_pool::task_thread_pool pool{4}; // num_threads = 4

Submit a function, a lambda, std::packaged_task, std::function, or any Callable, and its arguments:

pool.submit_detach( [](int arg) { std::cout << arg; }, 123456 );

To track task return values (and thrown exceptions), use submit() which returns an std::future:

std::future<int> future = pool.submit([] { return1; });
int result = future.get(); // returns 1

std::future::get() waits for the task to complete.

To wait for all tasks to complete:

pool.wait_for_tasks();

Parallel Loops and More

Use poolSTL to parallelize loops, transforms, sorts, and other standard library algorithms using this thread pool. This approach is easy to start with and also keeps your code future-proof by employing standard C++ mechanisms. It is easy to later change parallelism libraries (or start using the compiler-provided ones, once they're available to you).

For example, use std::for_each to parallelize for and for-each loops:

std::vector<int> v = {0, 1, 2, 3, 4, 5};
task_thread_pool::task_thread_pool pool;
// parallel forusing poolstl::iota_iter;
std::for_each(poolstl::par.on(pool), iota_iter<int>(0), iota_iter<int>(v.size()), [](int i) {
std::cout << v[i]; // loop body
});
// parallel for-eachstd::for_each(poolstl::par.on(pool), v.cbegin(), v.cend(), [](auto value) {
std::cout << value; // loop body
});

Example

#include<iostream>// Use #include "task_thread_pool.hpp" for relative path,// and #include <task_thread_pool.hpp> if installed in include path
#include"task_thread_pool.hpp"intsum(int a, int b) { return a + b; }
intmain() {
// Create a thread pool. The number of threads is equal to the number of cores in the system,// as given by std::thread::hardware_concurrency().// You can also specify the number of threads, like so: pool(4),// or resize the thread pool later using pool.set_num_threads(4).
task_thread_pool::task_thread_pool pool;
//---------------------------------------------// Submit a task that returns a value.
std::future<int> one_future = pool.submit([] { return1; });
// Use std::future::get() to wait for the task to complete and return the value.
std::cout << "Task returned: " << one_future.get() << std::endl;
//---------------------------------------------// Tasks may have arguments:
std::future<int> sum_future = pool.submit(&sum, 1, 2);
std::cout << "Sum = " << sum_future.get() << std::endl;
//---------------------------------------------// Submit a task that we don't need to track the execution of:
pool.submit_detach([](int arg) {
std::cout << "The argument is: " << arg << std::endl;
}, 42);
//---------------------------------------------// Wait for all tasks to complete:
pool.wait_for_tasks();
//---------------------------------------------// The pool can be paused:
pool.pause();
// Submit a task that won't be started until the pool is unpaused.
std::future<void> paused_future = pool.submit([] {
std::cout << "Paused task executes" << std::endl;
});
// prints 1
std::cout << "Number of tasks in the pool: " << pool.get_num_tasks() << std::endl;
// Resume executing queued tasks.
pool.unpause();
// Wait for the task to finish.
paused_future.get();
// prints 0
std::cout << "Number of tasks in the pool: " << pool.get_num_tasks() << std::endl;
//---------------------------------------------// All queued tasks are executed before the pool is destroyed:
pool.submit_detach([]{
std::cout << "One last task" << std::endl;
});
return0;
}

Installation

Copy

task-thread-pool is a single header file.

You may simply copy task_thread_pool.hpp into your project or your system include/.

CMake

You may use CMake to fetch directly from GitHub:

include(FetchContent)
FetchContent_Declare(
task-thread-pool
GIT_REPOSITORY https://github.com/alugowski/task-thread-pool
GIT_TAG main
GIT_SHALLOWTRUE
)
FetchContent_MakeAvailable(task-thread-pool)
target_link_libraries(YOUR_TARGETtask-thread-pool::task-thread-pool)

Use GIT_TAG main to use the latest version, or replace main with a version number to pin a fixed version.

vcpkg

vcpkg install task-thread-pool

Note for Clang and GCC <9 users

Some compilers, including non-Apple Clang and GCC 8 and older, require the -lpthread linker flag to use C++11 threads. The above CMake instructions will do that automatically.

How it works

Simplicity is a major goal so this thread pool does what you'd expect. Submitted tasks are added to a queue and worker threads pull from this queue.

Care is taken that this process is efficient. The submit methods are optimized to only do what they need. Worker threads only lock the queue once per task. Excess synchronization is avoided.

That said, this simple design is best used in low contention scenarios. If you have many tiny tasks or many (10+) physical CPU cores then this single queue becomes a hotspot. In that case avoid lightweight pools like this one and use something like Threading Building Blocks. They include work-stealing executors that avoid this hotspot at the cost of extra complexity and project dependencies.

Benchmarking

We include some Google Benchmarks for some pool operations in benchmark/.

If there is an operation you care about feel free to open an issue or submit your own benchmark code.

-------------------------------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------------------------------
pool_create_destroy 46318 ns 26004 ns 26489
submit_detach_packaged_task/paused:1 254 ns 244 ns 2875157
submit_detach_packaged_task/paused:0 362 ns 304 ns 2296008
submit_detach_void_lambda/paused:1 263 ns 260 ns 3072412
submit_detach_void_lambda/paused:0 418 ns 374 ns 2020779
submit_void_lambda/paused:1 399 ns 385 ns 1942879
submit_void_lambda/paused:0 667 ns 543 ns 1257161
submit_void_lambda_future/paused:1 391 ns 376 ns 1897255
submit_void_lambda_future/paused:0 649 ns 524 ns 1238653
submit_int_lambda_future/paused:1 395 ns 376 ns 1902789
submit_int_lambda_future/paused:0 643 ns 518 ns 1146038
run_1k_packaged_tasks 462965 ns 362080 ns 1939
run_1k_void_lambdas 492022 ns 411069 ns 1712
run_1k_int_lambdas 679579 ns 533813 ns 1368

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

testscodecovFile size in bytesLicenseLicenseLicense: MIT

task-thread-pool is a fast and lightweight thread pool for C++11 and newer.

Easily add parallelism to your project without introducing heavy dependencies.

  • Focus on correctness, ease of use, simplicity, performance.
  • Small single header file and permissive licensing means easy integration.
  • Tested on all major platforms and compilers:
    • Linux, macOS, Windows
    • GCC, LLVM/Clang, MSVC, MinGW, Emscripten
      • CI tests on GCC 7+ and LLVM 7+, should work on older
    • C++11, C++14, C++17, C++20, C++23
  • Comprehensive test suite, including stress tests.
  • Benchmarks help confirm good performance.

Usage

#include<task_thread_pool.hpp>
task_thread_pool::task_thread_pool pool; // num_threads = number of physical cores// or
task_thread_pool::task_thread_pool pool{4}; // num_threads = 4

Submit a function, a lambda, std::packaged_task, std::function, or any Callable, and its arguments:

pool.submit_detach( [](int arg) { std::cout << arg; }, 123456 );

To track task return values (and thrown exceptions), use submit() which returns an std::future:

std::future<int> future = pool.submit([] { return1; });
int result = future.get(); // returns 1

std::future::get() waits for the task to complete.

To wait for all tasks to complete:

pool.wait_for_tasks();

Parallel Loops and More

Use poolSTL to parallelize loops, transforms, sorts, and other standard library algorithms using this thread pool. This approach is easy to start with and also keeps your code future-proof by employing standard C++ mechanisms. It is easy to later change parallelism libraries (or start using the compiler-provided ones, once they're available to you).

For example, use std::for_each to parallelize for and for-each loops:

std::vector<int> v = {0, 1, 2, 3, 4, 5};
task_thread_pool::task_thread_pool pool;
// parallel forusing poolstl::iota_iter;
std::for_each(poolstl::par.on(pool), iota_iter<int>(0), iota_iter<int>(v.size()), [](int i) {
std::cout << v[i]; // loop body
});
// parallel for-eachstd::for_each(poolstl::par.on(pool), v.cbegin(), v.cend(), [](auto value) {
std::cout << value; // loop body
});

Example

#include<iostream>// Use #include "task_thread_pool.hpp" for relative path,// and #include <task_thread_pool.hpp> if installed in include path
#include"task_thread_pool.hpp"intsum(int a, int b) { return a + b; }
intmain() {
// Create a thread pool. The number of threads is equal to the number of cores in the system,// as given by std::thread::hardware_concurrency().// You can also specify the number of threads, like so: pool(4),// or resize the thread pool later using pool.set_num_threads(4).
task_thread_pool::task_thread_pool pool;
//---------------------------------------------// Submit a task that returns a value.
std::future<int> one_future = pool.submit([] { return1; });
// Use std::future::get() to wait for the task to complete and return the value.
std::cout << "Task returned: " << one_future.get() << std::endl;
//---------------------------------------------// Tasks may have arguments:
std::future<int> sum_future = pool.submit(&sum, 1, 2);
std::cout << "Sum = " << sum_future.get() << std::endl;
//---------------------------------------------// Submit a task that we don't need to track the execution of:
pool.submit_detach([](int arg) {
std::cout << "The argument is: " << arg << std::endl;
}, 42);
//---------------------------------------------// Wait for all tasks to complete:
pool.wait_for_tasks();
//---------------------------------------------// The pool can be paused:
pool.pause();
// Submit a task that won't be started until the pool is unpaused.
std::future<void> paused_future = pool.submit([] {
std::cout << "Paused task executes" << std::endl;
});
// prints 1
std::cout << "Number of tasks in the pool: " << pool.get_num_tasks() << std::endl;
// Resume executing queued tasks.
pool.unpause();
// Wait for the task to finish.
paused_future.get();
// prints 0
std::cout << "Number of tasks in the pool: " << pool.get_num_tasks() << std::endl;
//---------------------------------------------// All queued tasks are executed before the pool is destroyed:
pool.submit_detach([]{
std::cout << "One last task" << std::endl;
});
return0;
}

Installation

Copy

task-thread-pool is a single header file.

You may simply copy task_thread_pool.hpp into your project or your system include/.

CMake

You may use CMake to fetch directly from GitHub:

include(FetchContent)
FetchContent_Declare(
task-thread-pool
GIT_REPOSITORY https://github.com/alugowski/task-thread-pool
GIT_TAG main
GIT_SHALLOWTRUE
)
FetchContent_MakeAvailable(task-thread-pool)
target_link_libraries(YOUR_TARGETtask-thread-pool::task-thread-pool)

Use GIT_TAG main to use the latest version, or replace main with a version number to pin a fixed version.

vcpkg

vcpkg install task-thread-pool

Note for Clang and GCC <9 users

Some compilers, including non-Apple Clang and GCC 8 and older, require the -lpthread linker flag to use C++11 threads. The above CMake instructions will do that automatically.

How it works

Simplicity is a major goal so this thread pool does what you'd expect. Submitted tasks are added to a queue and worker threads pull from this queue.

Care is taken that this process is efficient. The submit methods are optimized to only do what they need. Worker threads only lock the queue once per task. Excess synchronization is avoided.

That said, this simple design is best used in low contention scenarios. If you have many tiny tasks or many (10+) physical CPU cores then this single queue becomes a hotspot. In that case avoid lightweight pools like this one and use something like Threading Building Blocks. They include work-stealing executors that avoid this hotspot at the cost of extra complexity and project dependencies.

Benchmarking

We include some Google Benchmarks for some pool operations in benchmark/.

If there is an operation you care about feel free to open an issue or submit your own benchmark code.

-------------------------------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------------------------------
pool_create_destroy 46318 ns 26004 ns 26489
submit_detach_packaged_task/paused:1 254 ns 244 ns 2875157
submit_detach_packaged_task/paused:0 362 ns 304 ns 2296008
submit_detach_void_lambda/paused:1 263 ns 260 ns 3072412
submit_detach_void_lambda/paused:0 418 ns 374 ns 2020779
submit_void_lambda/paused:1 399 ns 385 ns 1942879
submit_void_lambda/paused:0 667 ns 543 ns 1257161
submit_void_lambda_future/paused:1 391 ns 376 ns 1897255
submit_void_lambda_future/paused:0 649 ns 524 ns 1238653
submit_int_lambda_future/paused:1 395 ns 376 ns 1902789
submit_int_lambda_future/paused:0 643 ns 518 ns 1146038
run_1k_packaged_tasks 462965 ns 362080 ns 1939
run_1k_void_lambdas 492022 ns 411069 ns 1712
run_1k_int_lambdas 679579 ns 533813 ns 1368

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

testscodecovFile size in bytesLicenseLicenseLicense: MIT

task-thread-pool is a fast and lightweight thread pool for C++11 and newer.

Easily add parallelism to your project without introducing heavy dependencies.

  • Focus on correctness, ease of use, simplicity, performance.
  • Small single header file and permissive licensing means easy integration.
  • Tested on all major platforms and compilers:
    • Linux, macOS, Windows
    • GCC, LLVM/Clang, MSVC, MinGW, Emscripten
      • CI tests on GCC 7+ and LLVM 7+, should work on older
    • C++11, C++14, C++17, C++20, C++23
  • Comprehensive test suite, including stress tests.
  • Benchmarks help confirm good performance.

Usage

#include<task_thread_pool.hpp>
task_thread_pool::task_thread_pool pool; // num_threads = number of physical cores// or
task_thread_pool::task_thread_pool pool{4}; // num_threads = 4

Submit a function, a lambda, std::packaged_task, std::function, or any Callable, and its arguments:

pool.submit_detach( [](int arg) { std::cout << arg; }, 123456 );

To track task return values (and thrown exceptions), use submit() which returns an std::future:

std::future<int> future = pool.submit([] { return1; });
int result = future.get(); // returns 1

std::future::get() waits for the task to complete.

To wait for all tasks to complete:

pool.wait_for_tasks();

Parallel Loops and More

Use poolSTL to parallelize loops, transforms, sorts, and other standard library algorithms using this thread pool. This approach is easy to start with and also keeps your code future-proof by employing standard C++ mechanisms. It is easy to later change parallelism libraries (or start using the compiler-provided ones, once they're available to you).

For example, use std::for_each to parallelize for and for-each loops:

std::vector<int> v = {0, 1, 2, 3, 4, 5};
task_thread_pool::task_thread_pool pool;
// parallel forusing poolstl::iota_iter;
std::for_each(poolstl::par.on(pool), iota_iter<int>(0), iota_iter<int>(v.size()), [](int i) {
std::cout << v[i]; // loop body
});
// parallel for-eachstd::for_each(poolstl::par.on(pool), v.cbegin(), v.cend(), [](auto value) {
std::cout << value; // loop body
});

Example

#include<iostream>// Use #include "task_thread_pool.hpp" for relative path,// and #include <task_thread_pool.hpp> if installed in include path
#include"task_thread_pool.hpp"intsum(int a, int b) { return a + b; }
intmain() {
// Create a thread pool. The number of threads is equal to the number of cores in the system,// as given by std::thread::hardware_concurrency().// You can also specify the number of threads, like so: pool(4),// or resize the thread pool later using pool.set_num_threads(4).
task_thread_pool::task_thread_pool pool;
//---------------------------------------------// Submit a task that returns a value.
std::future<int> one_future = pool.submit([] { return1; });
// Use std::future::get() to wait for the task to complete and return the value.
std::cout << "Task returned: " << one_future.get() << std::endl;
//---------------------------------------------// Tasks may have arguments:
std::future<int> sum_future = pool.submit(&sum, 1, 2);
std::cout << "Sum = " << sum_future.get() << std::endl;
//---------------------------------------------// Submit a task that we don't need to track the execution of:
pool.submit_detach([](int arg) {
std::cout << "The argument is: " << arg << std::endl;
}, 42);
//---------------------------------------------// Wait for all tasks to complete:
pool.wait_for_tasks();
//---------------------------------------------// The pool can be paused:
pool.pause();
// Submit a task that won't be started until the pool is unpaused.
std::future<void> paused_future = pool.submit([] {
std::cout << "Paused task executes" << std::endl;
});
// prints 1
std::cout << "Number of tasks in the pool: " << pool.get_num_tasks() << std::endl;
// Resume executing queued tasks.
pool.unpause();
// Wait for the task to finish.
paused_future.get();
// prints 0
std::cout << "Number of tasks in the pool: " << pool.get_num_tasks() << std::endl;
//---------------------------------------------// All queued tasks are executed before the pool is destroyed:
pool.submit_detach([]{
std::cout << "One last task" << std::endl;
});
return0;
}

Installation

Copy

task-thread-pool is a single header file.

You may simply copy task_thread_pool.hpp into your project or your system include/.

CMake

You may use CMake to fetch directly from GitHub:

include(FetchContent)
FetchContent_Declare(
task-thread-pool
GIT_REPOSITORY https://github.com/alugowski/task-thread-pool
GIT_TAG main
GIT_SHALLOWTRUE
)
FetchContent_MakeAvailable(task-thread-pool)
target_link_libraries(YOUR_TARGETtask-thread-pool::task-thread-pool)

Use GIT_TAG main to use the latest version, or replace main with a version number to pin a fixed version.

vcpkg

vcpkg install task-thread-pool

Note for Clang and GCC <9 users

Some compilers, including non-Apple Clang and GCC 8 and older, require the -lpthread linker flag to use C++11 threads. The above CMake instructions will do that automatically.

How it works

Simplicity is a major goal so this thread pool does what you'd expect. Submitted tasks are added to a queue and worker threads pull from this queue.

Care is taken that this process is efficient. The submit methods are optimized to only do what they need. Worker threads only lock the queue once per task. Excess synchronization is avoided.

That said, this simple design is best used in low contention scenarios. If you have many tiny tasks or many (10+) physical CPU cores then this single queue becomes a hotspot. In that case avoid lightweight pools like this one and use something like Threading Building Blocks. They include work-stealing executors that avoid this hotspot at the cost of extra complexity and project dependencies.

Benchmarking

We include some Google Benchmarks for some pool operations in benchmark/.

If there is an operation you care about feel free to open an issue or submit your own benchmark code.

-------------------------------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------------------------------
pool_create_destroy 46318 ns 26004 ns 26489
submit_detach_packaged_task/paused:1 254 ns 244 ns 2875157
submit_detach_packaged_task/paused:0 362 ns 304 ns 2296008
submit_detach_void_lambda/paused:1 263 ns 260 ns 3072412
submit_detach_void_lambda/paused:0 418 ns 374 ns 2020779
submit_void_lambda/paused:1 399 ns 385 ns 1942879
submit_void_lambda/paused:0 667 ns 543 ns 1257161
submit_void_lambda_future/paused:1 391 ns 376 ns 1897255
submit_void_lambda_future/paused:0 649 ns 524 ns 1238653
submit_int_lambda_future/paused:1 395 ns 376 ns 1902789
submit_int_lambda_future/paused:0 643 ns 518 ns 1146038
run_1k_packaged_tasks 462965 ns 362080 ns 1939
run_1k_void_lambdas 492022 ns 411069 ns 1712
run_1k_int_lambdas 679579 ns 533813 ns 1368

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

testscodecovFile size in bytesLicenseLicenseLicense: MIT

task-thread-pool is a fast and lightweight thread pool for C++11 and newer.

Easily add parallelism to your project without introducing heavy dependencies.

  • Focus on correctness, ease of use, simplicity, performance.
  • Small single header file and permissive licensing means easy integration.
  • Tested on all major platforms and compilers:
    • Linux, macOS, Windows
    • GCC, LLVM/Clang, MSVC, MinGW, Emscripten
      • CI tests on GCC 7+ and LLVM 7+, should work on older
    • C++11, C++14, C++17, C++20, C++23
  • Comprehensive test suite, including stress tests.
  • Benchmarks help confirm good performance.

Usage

#include<task_thread_pool.hpp>
task_thread_pool::task_thread_pool pool; // num_threads = number of physical cores// or
task_thread_pool::task_thread_pool pool{4}; // num_threads = 4

Submit a function, a lambda, std::packaged_task, std::function, or any Callable, and its arguments:

pool.submit_detach( [](int arg) { std::cout << arg; }, 123456 );

To track task return values (and thrown exceptions), use submit() which returns an std::future:

std::future<int> future = pool.submit([] { return1; });
int result = future.get(); // returns 1

std::future::get() waits for the task to complete.

To wait for all tasks to complete:

pool.wait_for_tasks();

Parallel Loops and More

Use poolSTL to parallelize loops, transforms, sorts, and other standard library algorithms using this thread pool. This approach is easy to start with and also keeps your code future-proof by employing standard C++ mechanisms. It is easy to later change parallelism libraries (or start using the compiler-provided ones, once they're available to you).

For example, use std::for_each to parallelize for and for-each loops:

std::vector<int> v = {0, 1, 2, 3, 4, 5};
task_thread_pool::task_thread_pool pool;
// parallel forusing poolstl::iota_iter;
std::for_each(poolstl::par.on(pool), iota_iter<int>(0), iota_iter<int>(v.size()), [](int i) {
std::cout << v[i]; // loop body
});
// parallel for-eachstd::for_each(poolstl::par.on(pool), v.cbegin(), v.cend(), [](auto value) {
std::cout << value; // loop body
});

Example

#include<iostream>// Use #include "task_thread_pool.hpp" for relative path,// and #include <task_thread_pool.hpp> if installed in include path
#include"task_thread_pool.hpp"intsum(int a, int b) { return a + b; }
intmain() {
// Create a thread pool. The number of threads is equal to the number of cores in the system,// as given by std::thread::hardware_concurrency().// You can also specify the number of threads, like so: pool(4),// or resize the thread pool later using pool.set_num_threads(4).
task_thread_pool::task_thread_pool pool;
//---------------------------------------------// Submit a task that returns a value.
std::future<int> one_future = pool.submit([] { return1; });
// Use std::future::get() to wait for the task to complete and return the value.
std::cout << "Task returned: " << one_future.get() << std::endl;
//---------------------------------------------// Tasks may have arguments:
std::future<int> sum_future = pool.submit(&sum, 1, 2);
std::cout << "Sum = " << sum_future.get() << std::endl;
//---------------------------------------------// Submit a task that we don't need to track the execution of:
pool.submit_detach([](int arg) {
std::cout << "The argument is: " << arg << std::endl;
}, 42);
//---------------------------------------------// Wait for all tasks to complete:
pool.wait_for_tasks();
//---------------------------------------------// The pool can be paused:
pool.pause();
// Submit a task that won't be started until the pool is unpaused.
std::future<void> paused_future = pool.submit([] {
std::cout << "Paused task executes" << std::endl;
});
// prints 1
std::cout << "Number of tasks in the pool: " << pool.get_num_tasks() << std::endl;
// Resume executing queued tasks.
pool.unpause();
// Wait for the task to finish.
paused_future.get();
// prints 0
std::cout << "Number of tasks in the pool: " << pool.get_num_tasks() << std::endl;
//---------------------------------------------// All queued tasks are executed before the pool is destroyed:
pool.submit_detach([]{
std::cout << "One last task" << std::endl;
});
return0;
}

Installation

Copy

task-thread-pool is a single header file.

You may simply copy task_thread_pool.hpp into your project or your system include/.

CMake

You may use CMake to fetch directly from GitHub:

include(FetchContent)
FetchContent_Declare(
task-thread-pool
GIT_REPOSITORY https://github.com/alugowski/task-thread-pool
GIT_TAG main
GIT_SHALLOWTRUE
)
FetchContent_MakeAvailable(task-thread-pool)
target_link_libraries(YOUR_TARGETtask-thread-pool::task-thread-pool)

Use GIT_TAG main to use the latest version, or replace main with a version number to pin a fixed version.

vcpkg

vcpkg install task-thread-pool

Note for Clang and GCC <9 users

Some compilers, including non-Apple Clang and GCC 8 and older, require the -lpthread linker flag to use C++11 threads. The above CMake instructions will do that automatically.

How it works

Simplicity is a major goal so this thread pool does what you'd expect. Submitted tasks are added to a queue and worker threads pull from this queue.

Care is taken that this process is efficient. The submit methods are optimized to only do what they need. Worker threads only lock the queue once per task. Excess synchronization is avoided.

That said, this simple design is best used in low contention scenarios. If you have many tiny tasks or many (10+) physical CPU cores then this single queue becomes a hotspot. In that case avoid lightweight pools like this one and use something like Threading Building Blocks. They include work-stealing executors that avoid this hotspot at the cost of extra complexity and project dependencies.

Benchmarking

We include some Google Benchmarks for some pool operations in benchmark/.

If there is an operation you care about feel free to open an issue or submit your own benchmark code.

-------------------------------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------------------------------
pool_create_destroy 46318 ns 26004 ns 26489
submit_detach_packaged_task/paused:1 254 ns 244 ns 2875157
submit_detach_packaged_task/paused:0 362 ns 304 ns 2296008
submit_detach_void_lambda/paused:1 263 ns 260 ns 3072412
submit_detach_void_lambda/paused:0 418 ns 374 ns 2020779
submit_void_lambda/paused:1 399 ns 385 ns 1942879
submit_void_lambda/paused:0 667 ns 543 ns 1257161
submit_void_lambda_future/paused:1 391 ns 376 ns 1897255
submit_void_lambda_future/paused:0 649 ns 524 ns 1238653
submit_int_lambda_future/paused:1 395 ns 376 ns 1902789
submit_int_lambda_future/paused:0 643 ns 518 ns 1146038
run_1k_packaged_tasks 462965 ns 362080 ns 1939
run_1k_void_lambdas 492022 ns 411069 ns 1712
run_1k_int_lambdas 679579 ns 533813 ns 1368

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

testscodecovFile size in bytesLicenseLicenseLicense: MIT

task-thread-pool is a fast and lightweight thread pool for C++11 and newer.

Easily add parallelism to your project without introducing heavy dependencies.

  • Focus on correctness, ease of use, simplicity, performance.
  • Small single header file and permissive licensing means easy integration.
  • Tested on all major platforms and compilers:
    • Linux, macOS, Windows
    • GCC, LLVM/Clang, MSVC, MinGW, Emscripten
      • CI tests on GCC 7+ and LLVM 7+, should work on older
    • C++11, C++14, C++17, C++20, C++23
  • Comprehensive test suite, including stress tests.
  • Benchmarks help confirm good performance.

Usage

#include<task_thread_pool.hpp>
task_thread_pool::task_thread_pool pool; // num_threads = number of physical cores// or
task_thread_pool::task_thread_pool pool{4}; // num_threads = 4

Submit a function, a lambda, std::packaged_task, std::function, or any Callable, and its arguments:

pool.submit_detach( [](int arg) { std::cout << arg; }, 123456 );

To track task return values (and thrown exceptions), use submit() which returns an std::future:

std::future<int> future = pool.submit([] { return1; });
int result = future.get(); // returns 1

std::future::get() waits for the task to complete.

To wait for all tasks to complete:

pool.wait_for_tasks();

Parallel Loops and More

Use poolSTL to parallelize loops, transforms, sorts, and other standard library algorithms using this thread pool. This approach is easy to start with and also keeps your code future-proof by employing standard C++ mechanisms. It is easy to later change parallelism libraries (or start using the compiler-provided ones, once they're available to you).

For example, use std::for_each to parallelize for and for-each loops:

std::vector<int> v = {0, 1, 2, 3, 4, 5};
task_thread_pool::task_thread_pool pool;
// parallel forusing poolstl::iota_iter;
std::for_each(poolstl::par.on(pool), iota_iter<int>(0), iota_iter<int>(v.size()), [](int i) {
std::cout << v[i]; // loop body
});
// parallel for-eachstd::for_each(poolstl::par.on(pool), v.cbegin(), v.cend(), [](auto value) {
std::cout << value; // loop body
});

Example

#include<iostream>// Use #include "task_thread_pool.hpp" for relative path,// and #include <task_thread_pool.hpp> if installed in include path
#include"task_thread_pool.hpp"intsum(int a, int b) { return a + b; }
intmain() {
// Create a thread pool. The number of threads is equal to the number of cores in the system,// as given by std::thread::hardware_concurrency().// You can also specify the number of threads, like so: pool(4),// or resize the thread pool later using pool.set_num_threads(4).
task_thread_pool::task_thread_pool pool;
//---------------------------------------------// Submit a task that returns a value.
std::future<int> one_future = pool.submit([] { return1; });
// Use std::future::get() to wait for the task to complete and return the value.
std::cout << "Task returned: " << one_future.get() << std::endl;
//---------------------------------------------// Tasks may have arguments:
std::future<int> sum_future = pool.submit(&sum, 1, 2);
std::cout << "Sum = " << sum_future.get() << std::endl;
//---------------------------------------------// Submit a task that we don't need to track the execution of:
pool.submit_detach([](int arg) {
std::cout << "The argument is: " << arg << std::endl;
}, 42);
//---------------------------------------------// Wait for all tasks to complete:
pool.wait_for_tasks();
//---------------------------------------------// The pool can be paused:
pool.pause();
// Submit a task that won't be started until the pool is unpaused.
std::future<void> paused_future = pool.submit([] {
std::cout << "Paused task executes" << std::endl;
});
// prints 1
std::cout << "Number of tasks in the pool: " << pool.get_num_tasks() << std::endl;
// Resume executing queued tasks.
pool.unpause();
// Wait for the task to finish.
paused_future.get();
// prints 0
std::cout << "Number of tasks in the pool: " << pool.get_num_tasks() << std::endl;
//---------------------------------------------// All queued tasks are executed before the pool is destroyed:
pool.submit_detach([]{
std::cout << "One last task" << std::endl;
});
return0;
}

Installation

Copy

task-thread-pool is a single header file.

You may simply copy task_thread_pool.hpp into your project or your system include/.

CMake

You may use CMake to fetch directly from GitHub:

include(FetchContent)
FetchContent_Declare(
task-thread-pool
GIT_REPOSITORY https://github.com/alugowski/task-thread-pool
GIT_TAG main
GIT_SHALLOWTRUE
)
FetchContent_MakeAvailable(task-thread-pool)
target_link_libraries(YOUR_TARGETtask-thread-pool::task-thread-pool)

Use GIT_TAG main to use the latest version, or replace main with a version number to pin a fixed version.

vcpkg

vcpkg install task-thread-pool

Note for Clang and GCC <9 users

Some compilers, including non-Apple Clang and GCC 8 and older, require the -lpthread linker flag to use C++11 threads. The above CMake instructions will do that automatically.

How it works

Simplicity is a major goal so this thread pool does what you'd expect. Submitted tasks are added to a queue and worker threads pull from this queue.

Care is taken that this process is efficient. The submit methods are optimized to only do what they need. Worker threads only lock the queue once per task. Excess synchronization is avoided.

That said, this simple design is best used in low contention scenarios. If you have many tiny tasks or many (10+) physical CPU cores then this single queue becomes a hotspot. In that case avoid lightweight pools like this one and use something like Threading Building Blocks. They include work-stealing executors that avoid this hotspot at the cost of extra complexity and project dependencies.

Benchmarking

We include some Google Benchmarks for some pool operations in benchmark/.

If there is an operation you care about feel free to open an issue or submit your own benchmark code.

-------------------------------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------------------------------
pool_create_destroy 46318 ns 26004 ns 26489
submit_detach_packaged_task/paused:1 254 ns 244 ns 2875157
submit_detach_packaged_task/paused:0 362 ns 304 ns 2296008
submit_detach_void_lambda/paused:1 263 ns 260 ns 3072412
submit_detach_void_lambda/paused:0 418 ns 374 ns 2020779
submit_void_lambda/paused:1 399 ns 385 ns 1942879
submit_void_lambda/paused:0 667 ns 543 ns 1257161
submit_void_lambda_future/paused:1 391 ns 376 ns 1897255
submit_void_lambda_future/paused:0 649 ns 524 ns 1238653
submit_int_lambda_future/paused:1 395 ns 376 ns 1902789
submit_int_lambda_future/paused:0 643 ns 518 ns 1146038
run_1k_packaged_tasks 462965 ns 362080 ns 1939
run_1k_void_lambdas 492022 ns 411069 ns 1712
run_1k_int_lambdas 679579 ns 533813 ns 1368

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

testscodecovFile size in bytesLicenseLicenseLicense: MIT

task-thread-pool is a fast and lightweight thread pool for C++11 and newer.

Easily add parallelism to your project without introducing heavy dependencies.

  • Focus on correctness, ease of use, simplicity, performance.
  • Small single header file and permissive licensing means easy integration.
  • Tested on all major platforms and compilers:
    • Linux, macOS, Windows
    • GCC, LLVM/Clang, MSVC, MinGW, Emscripten
      • CI tests on GCC 7+ and LLVM 7+, should work on older
    • C++11, C++14, C++17, C++20, C++23
  • Comprehensive test suite, including stress tests.
  • Benchmarks help confirm good performance.

Usage

#include<task_thread_pool.hpp>
task_thread_pool::task_thread_pool pool; // num_threads = number of physical cores// or
task_thread_pool::task_thread_pool pool{4}; // num_threads = 4

Submit a function, a lambda, std::packaged_task, std::function, or any Callable, and its arguments:

pool.submit_detach( [](int arg) { std::cout << arg; }, 123456 );

To track task return values (and thrown exceptions), use submit() which returns an std::future:

std::future<int> future = pool.submit([] { return1; });
int result = future.get(); // returns 1

std::future::get() waits for the task to complete.

To wait for all tasks to complete:

pool.wait_for_tasks();

Parallel Loops and More

Use poolSTL to parallelize loops, transforms, sorts, and other standard library algorithms using this thread pool. This approach is easy to start with and also keeps your code future-proof by employing standard C++ mechanisms. It is easy to later change parallelism libraries (or start using the compiler-provided ones, once they're available to you).

For example, use std::for_each to parallelize for and for-each loops:

std::vector<int> v = {0, 1, 2, 3, 4, 5};
task_thread_pool::task_thread_pool pool;
// parallel forusing poolstl::iota_iter;
std::for_each(poolstl::par.on(pool), iota_iter<int>(0), iota_iter<int>(v.size()), [](int i) {
std::cout << v[i]; // loop body
});
// parallel for-eachstd::for_each(poolstl::par.on(pool), v.cbegin(), v.cend(), [](auto value) {
std::cout << value; // loop body
});

Example

#include<iostream>// Use #include "task_thread_pool.hpp" for relative path,// and #include <task_thread_pool.hpp> if installed in include path
#include"task_thread_pool.hpp"intsum(int a, int b) { return a + b; }
intmain() {
// Create a thread pool. The number of threads is equal to the number of cores in the system,// as given by std::thread::hardware_concurrency().// You can also specify the number of threads, like so: pool(4),// or resize the thread pool later using pool.set_num_threads(4).
task_thread_pool::task_thread_pool pool;
//---------------------------------------------// Submit a task that returns a value.
std::future<int> one_future = pool.submit([] { return1; });
// Use std::future::get() to wait for the task to complete and return the value.
std::cout << "Task returned: " << one_future.get() << std::endl;
//---------------------------------------------// Tasks may have arguments:
std::future<int> sum_future = pool.submit(&sum, 1, 2);
std::cout << "Sum = " << sum_future.get() << std::endl;
//---------------------------------------------// Submit a task that we don't need to track the execution of:
pool.submit_detach([](int arg) {
std::cout << "The argument is: " << arg << std::endl;
}, 42);
//---------------------------------------------// Wait for all tasks to complete:
pool.wait_for_tasks();
//---------------------------------------------// The pool can be paused:
pool.pause();
// Submit a task that won't be started until the pool is unpaused.
std::future<void> paused_future = pool.submit([] {
std::cout << "Paused task executes" << std::endl;
});
// prints 1
std::cout << "Number of tasks in the pool: " << pool.get_num_tasks() << std::endl;
// Resume executing queued tasks.
pool.unpause();
// Wait for the task to finish.
paused_future.get();
// prints 0
std::cout << "Number of tasks in the pool: " << pool.get_num_tasks() << std::endl;
//---------------------------------------------// All queued tasks are executed before the pool is destroyed:
pool.submit_detach([]{
std::cout << "One last task" << std::endl;
});
return0;
}

Installation

Copy

task-thread-pool is a single header file.

You may simply copy task_thread_pool.hpp into your project or your system include/.

CMake

You may use CMake to fetch directly from GitHub:

include(FetchContent)
FetchContent_Declare(
task-thread-pool
GIT_REPOSITORY https://github.com/alugowski/task-thread-pool
GIT_TAG main
GIT_SHALLOWTRUE
)
FetchContent_MakeAvailable(task-thread-pool)
target_link_libraries(YOUR_TARGETtask-thread-pool::task-thread-pool)

Use GIT_TAG main to use the latest version, or replace main with a version number to pin a fixed version.

vcpkg

vcpkg install task-thread-pool

Note for Clang and GCC <9 users

Some compilers, including non-Apple Clang and GCC 8 and older, require the -lpthread linker flag to use C++11 threads. The above CMake instructions will do that automatically.

How it works

Simplicity is a major goal so this thread pool does what you'd expect. Submitted tasks are added to a queue and worker threads pull from this queue.

Care is taken that this process is efficient. The submit methods are optimized to only do what they need. Worker threads only lock the queue once per task. Excess synchronization is avoided.

That said, this simple design is best used in low contention scenarios. If you have many tiny tasks or many (10+) physical CPU cores then this single queue becomes a hotspot. In that case avoid lightweight pools like this one and use something like Threading Building Blocks. They include work-stealing executors that avoid this hotspot at the cost of extra complexity and project dependencies.

Benchmarking

We include some Google Benchmarks for some pool operations in benchmark/.

If there is an operation you care about feel free to open an issue or submit your own benchmark code.

-------------------------------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------------------------------
pool_create_destroy 46318 ns 26004 ns 26489
submit_detach_packaged_task/paused:1 254 ns 244 ns 2875157
submit_detach_packaged_task/paused:0 362 ns 304 ns 2296008
submit_detach_void_lambda/paused:1 263 ns 260 ns 3072412
submit_detach_void_lambda/paused:0 418 ns 374 ns 2020779
submit_void_lambda/paused:1 399 ns 385 ns 1942879
submit_void_lambda/paused:0 667 ns 543 ns 1257161
submit_void_lambda_future/paused:1 391 ns 376 ns 1897255
submit_void_lambda_future/paused:0 649 ns 524 ns 1238653
submit_int_lambda_future/paused:1 395 ns 376 ns 1902789
submit_int_lambda_future/paused:0 643 ns 518 ns 1146038
run_1k_packaged_tasks 462965 ns 362080 ns 1939
run_1k_void_lambdas 492022 ns 411069 ns 1712
run_1k_int_lambdas 679579 ns 533813 ns 1368

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

testscodecovFile size in bytesLicenseLicenseLicense: MIT

task-thread-pool is a fast and lightweight thread pool for C++11 and newer.

Easily add parallelism to your project without introducing heavy dependencies.

  • Focus on correctness, ease of use, simplicity, performance.
  • Small single header file and permissive licensing means easy integration.
  • Tested on all major platforms and compilers:
    • Linux, macOS, Windows
    • GCC, LLVM/Clang, MSVC, MinGW, Emscripten
      • CI tests on GCC 7+ and LLVM 7+, should work on older
    • C++11, C++14, C++17, C++20, C++23
  • Comprehensive test suite, including stress tests.
  • Benchmarks help confirm good performance.

Usage

#include<task_thread_pool.hpp>
task_thread_pool::task_thread_pool pool; // num_threads = number of physical cores// or
task_thread_pool::task_thread_pool pool{4}; // num_threads = 4

Submit a function, a lambda, std::packaged_task, std::function, or any Callable, and its arguments:

pool.submit_detach( [](int arg) { std::cout << arg; }, 123456 );

To track task return values (and thrown exceptions), use submit() which returns an std::future:

std::future<int> future = pool.submit([] { return1; });
int result = future.get(); // returns 1

std::future::get() waits for the task to complete.

To wait for all tasks to complete:

pool.wait_for_tasks();

Parallel Loops and More

Use poolSTL to parallelize loops, transforms, sorts, and other standard library algorithms using this thread pool. This approach is easy to start with and also keeps your code future-proof by employing standard C++ mechanisms. It is easy to later change parallelism libraries (or start using the compiler-provided ones, once they're available to you).

For example, use std::for_each to parallelize for and for-each loops:

std::vector<int> v = {0, 1, 2, 3, 4, 5};
task_thread_pool::task_thread_pool pool;
// parallel forusing poolstl::iota_iter;
std::for_each(poolstl::par.on(pool), iota_iter<int>(0), iota_iter<int>(v.size()), [](int i) {
std::cout << v[i]; // loop body
});
// parallel for-eachstd::for_each(poolstl::par.on(pool), v.cbegin(), v.cend(), [](auto value) {
std::cout << value; // loop body
});

Example

#include<iostream>// Use #include "task_thread_pool.hpp" for relative path,// and #include <task_thread_pool.hpp> if installed in include path
#include"task_thread_pool.hpp"intsum(int a, int b) { return a + b; }
intmain() {
// Create a thread pool. The number of threads is equal to the number of cores in the system,// as given by std::thread::hardware_concurrency().// You can also specify the number of threads, like so: pool(4),// or resize the thread pool later using pool.set_num_threads(4).
task_thread_pool::task_thread_pool pool;
//---------------------------------------------// Submit a task that returns a value.
std::future<int> one_future = pool.submit([] { return1; });
// Use std::future::get() to wait for the task to complete and return the value.
std::cout << "Task returned: " << one_future.get() << std::endl;
//---------------------------------------------// Tasks may have arguments:
std::future<int> sum_future = pool.submit(&sum, 1, 2);
std::cout << "Sum = " << sum_future.get() << std::endl;
//---------------------------------------------// Submit a task that we don't need to track the execution of:
pool.submit_detach([](int arg) {
std::cout << "The argument is: " << arg << std::endl;
}, 42);
//---------------------------------------------// Wait for all tasks to complete:
pool.wait_for_tasks();
//---------------------------------------------// The pool can be paused:
pool.pause();
// Submit a task that won't be started until the pool is unpaused.
std::future<void> paused_future = pool.submit([] {
std::cout << "Paused task executes" << std::endl;
});
// prints 1
std::cout << "Number of tasks in the pool: " << pool.get_num_tasks() << std::endl;
// Resume executing queued tasks.
pool.unpause();
// Wait for the task to finish.
paused_future.get();
// prints 0
std::cout << "Number of tasks in the pool: " << pool.get_num_tasks() << std::endl;
//---------------------------------------------// All queued tasks are executed before the pool is destroyed:
pool.submit_detach([]{
std::cout << "One last task" << std::endl;
});
return0;
}

Installation

Copy

task-thread-pool is a single header file.

You may simply copy task_thread_pool.hpp into your project or your system include/.

CMake

You may use CMake to fetch directly from GitHub:

include(FetchContent)
FetchContent_Declare(
task-thread-pool
GIT_REPOSITORY https://github.com/alugowski/task-thread-pool
GIT_TAG main
GIT_SHALLOWTRUE
)
FetchContent_MakeAvailable(task-thread-pool)
target_link_libraries(YOUR_TARGETtask-thread-pool::task-thread-pool)

Use GIT_TAG main to use the latest version, or replace main with a version number to pin a fixed version.

vcpkg

vcpkg install task-thread-pool

Note for Clang and GCC <9 users

Some compilers, including non-Apple Clang and GCC 8 and older, require the -lpthread linker flag to use C++11 threads. The above CMake instructions will do that automatically.

How it works

Simplicity is a major goal so this thread pool does what you'd expect. Submitted tasks are added to a queue and worker threads pull from this queue.

Care is taken that this process is efficient. The submit methods are optimized to only do what they need. Worker threads only lock the queue once per task. Excess synchronization is avoided.

That said, this simple design is best used in low contention scenarios. If you have many tiny tasks or many (10+) physical CPU cores then this single queue becomes a hotspot. In that case avoid lightweight pools like this one and use something like Threading Building Blocks. They include work-stealing executors that avoid this hotspot at the cost of extra complexity and project dependencies.

Benchmarking

We include some Google Benchmarks for some pool operations in benchmark/.

If there is an operation you care about feel free to open an issue or submit your own benchmark code.

-------------------------------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------------------------------
pool_create_destroy 46318 ns 26004 ns 26489
submit_detach_packaged_task/paused:1 254 ns 244 ns 2875157
submit_detach_packaged_task/paused:0 362 ns 304 ns 2296008
submit_detach_void_lambda/paused:1 263 ns 260 ns 3072412
submit_detach_void_lambda/paused:0 418 ns 374 ns 2020779
submit_void_lambda/paused:1 399 ns 385 ns 1942879
submit_void_lambda/paused:0 667 ns 543 ns 1257161
submit_void_lambda_future/paused:1 391 ns 376 ns 1897255
submit_void_lambda_future/paused:0 649 ns 524 ns 1238653
submit_int_lambda_future/paused:1 395 ns 376 ns 1902789
submit_int_lambda_future/paused:0 643 ns 518 ns 1146038
run_1k_packaged_tasks 462965 ns 362080 ns 1939
run_1k_void_lambdas 492022 ns 411069 ns 1712
run_1k_int_lambdas 679579 ns 533813 ns 1368

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

testscodecovFile size in bytesLicenseLicenseLicense: MIT

task-thread-pool is a fast and lightweight thread pool for C++11 and newer.

Easily add parallelism to your project without introducing heavy dependencies.

  • Focus on correctness, ease of use, simplicity, performance.
  • Small single header file and permissive licensing means easy integration.
  • Tested on all major platforms and compilers:
    • Linux, macOS, Windows
    • GCC, LLVM/Clang, MSVC, MinGW, Emscripten
      • CI tests on GCC 7+ and LLVM 7+, should work on older
    • C++11, C++14, C++17, C++20, C++23
  • Comprehensive test suite, including stress tests.
  • Benchmarks help confirm good performance.

Usage

#include<task_thread_pool.hpp>
task_thread_pool::task_thread_pool pool; // num_threads = number of physical cores// or
task_thread_pool::task_thread_pool pool{4}; // num_threads = 4

Submit a function, a lambda, std::packaged_task, std::function, or any Callable, and its arguments:

pool.submit_detach( [](int arg) { std::cout << arg; }, 123456 );

To track task return values (and thrown exceptions), use submit() which returns an std::future:

std::future<int> future = pool.submit([] { return1; });
int result = future.get(); // returns 1

std::future::get() waits for the task to complete.

To wait for all tasks to complete:

pool.wait_for_tasks();

Parallel Loops and More

Use poolSTL to parallelize loops, transforms, sorts, and other standard library algorithms using this thread pool. This approach is easy to start with and also keeps your code future-proof by employing standard C++ mechanisms. It is easy to later change parallelism libraries (or start using the compiler-provided ones, once they're available to you).

For example, use std::for_each to parallelize for and for-each loops:

std::vector<int> v = {0, 1, 2, 3, 4, 5};
task_thread_pool::task_thread_pool pool;
// parallel forusing poolstl::iota_iter;
std::for_each(poolstl::par.on(pool), iota_iter<int>(0), iota_iter<int>(v.size()), [](int i) {
std::cout << v[i]; // loop body
});
// parallel for-eachstd::for_each(poolstl::par.on(pool), v.cbegin(), v.cend(), [](auto value) {
std::cout << value; // loop body
});

Example

#include<iostream>// Use #include "task_thread_pool.hpp" for relative path,// and #include <task_thread_pool.hpp> if installed in include path
#include"task_thread_pool.hpp"intsum(int a, int b) { return a + b; }
intmain() {
// Create a thread pool. The number of threads is equal to the number of cores in the system,// as given by std::thread::hardware_concurrency().// You can also specify the number of threads, like so: pool(4),// or resize the thread pool later using pool.set_num_threads(4).
task_thread_pool::task_thread_pool pool;
//---------------------------------------------// Submit a task that returns a value.
std::future<int> one_future = pool.submit([] { return1; });
// Use std::future::get() to wait for the task to complete and return the value.
std::cout << "Task returned: " << one_future.get() << std::endl;
//---------------------------------------------// Tasks may have arguments:
std::future<int> sum_future = pool.submit(&sum, 1, 2);
std::cout << "Sum = " << sum_future.get() << std::endl;
//---------------------------------------------// Submit a task that we don't need to track the execution of:
pool.submit_detach([](int arg) {
std::cout << "The argument is: " << arg << std::endl;
}, 42);
//---------------------------------------------// Wait for all tasks to complete:
pool.wait_for_tasks();
//---------------------------------------------// The pool can be paused:
pool.pause();
// Submit a task that won't be started until the pool is unpaused.
std::future<void> paused_future = pool.submit([] {
std::cout << "Paused task executes" << std::endl;
});
// prints 1
std::cout << "Number of tasks in the pool: " << pool.get_num_tasks() << std::endl;
// Resume executing queued tasks.
pool.unpause();
// Wait for the task to finish.
paused_future.get();
// prints 0
std::cout << "Number of tasks in the pool: " << pool.get_num_tasks() << std::endl;
//---------------------------------------------// All queued tasks are executed before the pool is destroyed:
pool.submit_detach([]{
std::cout << "One last task" << std::endl;
});
return0;
}

Installation

Copy

task-thread-pool is a single header file.

You may simply copy task_thread_pool.hpp into your project or your system include/.

CMake

You may use CMake to fetch directly from GitHub:

include(FetchContent)
FetchContent_Declare(
task-thread-pool
GIT_REPOSITORY https://github.com/alugowski/task-thread-pool
GIT_TAG main
GIT_SHALLOWTRUE
)
FetchContent_MakeAvailable(task-thread-pool)
target_link_libraries(YOUR_TARGETtask-thread-pool::task-thread-pool)

Use GIT_TAG main to use the latest version, or replace main with a version number to pin a fixed version.

vcpkg

vcpkg install task-thread-pool

Note for Clang and GCC <9 users

Some compilers, including non-Apple Clang and GCC 8 and older, require the -lpthread linker flag to use C++11 threads. The above CMake instructions will do that automatically.

How it works

Simplicity is a major goal so this thread pool does what you'd expect. Submitted tasks are added to a queue and worker threads pull from this queue.

Care is taken that this process is efficient. The submit methods are optimized to only do what they need. Worker threads only lock the queue once per task. Excess synchronization is avoided.

That said, this simple design is best used in low contention scenarios. If you have many tiny tasks or many (10+) physical CPU cores then this single queue becomes a hotspot. In that case avoid lightweight pools like this one and use something like Threading Building Blocks. They include work-stealing executors that avoid this hotspot at the cost of extra complexity and project dependencies.

Benchmarking

We include some Google Benchmarks for some pool operations in benchmark/.

If there is an operation you care about feel free to open an issue or submit your own benchmark code.

-------------------------------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------------------------------
pool_create_destroy 46318 ns 26004 ns 26489
submit_detach_packaged_task/paused:1 254 ns 244 ns 2875157
submit_detach_packaged_task/paused:0 362 ns 304 ns 2296008
submit_detach_void_lambda/paused:1 263 ns 260 ns 3072412
submit_detach_void_lambda/paused:0 418 ns 374 ns 2020779
submit_void_lambda/paused:1 399 ns 385 ns 1942879
submit_void_lambda/paused:0 667 ns 543 ns 1257161
submit_void_lambda_future/paused:1 391 ns 376 ns 1897255
submit_void_lambda_future/paused:0 649 ns 524 ns 1238653
submit_int_lambda_future/paused:1 395 ns 376 ns 1902789
submit_int_lambda_future/paused:0 643 ns 518 ns 1146038
run_1k_packaged_tasks 462965 ns 362080 ns 1939
run_1k_void_lambdas 492022 ns 411069 ns 1712
run_1k_int_lambdas 679579 ns 533813 ns 1368

Releases

Packages

Used by

Contributors

Languages