I have a thread_pool class, that mimics std::thread. (I would have liked std to have a pool, but alas that is not the case.)
thread_pool.h
#ifndef _C9Y_THREAD_POOL_H_
#define _C9Y_THREAD_POOL_H_
#include <functional>
#include <thread>
#include <vector>
#include "defines.h"
namespace c9y
{
//! Thread Pool
class C9Y_EXPORT thread_pool
{
public:
//! Contruct empty thread pool.
//!
//! The default constructor will create a thread pool, but with no threads.
//! It can be used to create an instance on the stack that can later accept
//! a running thread pool with move assignment.
thread_pool() noexcept;
//! Create a thread pool.
//!
//! @param thread_func The thread function each thread in the pool executes.
//! @param concurency The number of threads to run concurently.
//!
//! This constructor will create @arg concurency threads that execute @arg thread_func.
thread_pool(std::function<void ()> thread_func, size_t concurency);
//! Move Constructor
thread_pool(thread_pool&& other) noexcept;
//! Destructor
//!
//! @warning If the thread pool is not joined, the underlying std::thread
//! objects will invoke std::terminate.
~thread_pool();
//! Move Asignment
thread_pool& operator = (thread_pool&& other) noexcept;
//! Get the concurency of the thread pool.
size_t get_concurency() const;
//! Join each thread in the pool.
//!
//! Calling join ensures that each thread function terminates and
//! the coresponsing memory is freed.
void join();
//!
//! Swap thread pool.
void swap(thread_pool& other) noexcept;
private:
std::vector<std::thread> threads;
thread_pool(const thread_pool&) = delete;
thread_pool& operator = (const thread_pool&) = delete;
};
}
#endif
thread_pool.cpp:
#include "thread_pool.h"
namespace c9y
{
thread_pool::thread_pool() noexcept {}
thread_pool::thread_pool(std::function<void()> thread_func, size_t concurency)
{
for (size_t i = 0; i < concurency; i++)
{
threads.emplace_back(std::thread(thread_func));
}
}
thread_pool::thread_pool(thread_pool&& other) noexcept
{
swap(other);
}
thread_pool::~thread_pool() {}
thread_pool& thread_pool::operator = (thread_pool&& other) noexcept
{
swap(other);
return *this;
}
size_t thread_pool::get_concurency() const
{
return threads.size();
}
void thread_pool::join()
{
for (auto& thread : threads)
{
thread.join();
}
threads.clear();
}
void thread_pool::swap(thread_pool& other) noexcept
{
threads.swap(other.threads);
}
}
thread_pool_test.cpp
#include <c9y/thread_pool.h>
#include <atomic>
#include <gtest/gtest.h>
TEST(thread_pool, create)
{
auto count = std::atomic<unsigned int>{0};
auto pool = c9y::thread_pool{[&]() {
count++;
}, 2};
pool.join();
EXPECT_EQ(2, static_cast<unsigned int>(count));
}
TEST(thread_pool, default_contructor)
{
auto pool = c9y::thread_pool{};
}
TEST(thread_pool, move)
{
auto count = std::atomic<unsigned int>{0};
auto pool = c9y::thread_pool{};
pool = c9y::thread_pool{[&]() {
count++;
}, 2};
pool.join();
EXPECT_EQ(2, static_cast<unsigned int>(count));
}
This code was originally written against C++11 but the library recently was updated to C++20 and I am in the process up "updating" the code; so please consider it in the light of C++20.
vector
of threads. \$\endgroup\$