国外的线程池库;
作者:Barak Shoshany (baraksh@gmail.com) (http://baraksh.com)
BS命名空间
Barak Shoshany这个作者的名字简写。
decltype(std::jthread::hardware_concurrency)
concurrency---并发
`std::jthread::hardware_concurrency`是C++20引入的一个新的标准库函数,用于返回当前系统支持的线程并发数(也就是CPU核心数)。这个函数的定义如下:
static unsigned int hardware_concurrency() noexcept;
因此,表达式`std::jthread::hardware_concurrency`的类型是`static unsigned int (*)() noexcept`,它表示一个不接受参数、返回无符号整数、不抛出异常的静态函数指针。将这个类型作为`std::invoke_result_t`模板的参数,就可以获取函数的返回值类型,即`unsigned int`。
std::invoke_result_t<>
std::invoke_result_t`是C++17引入的一个模板,用于获取一个函数或成员指针的返回值类型。
decltype---获取一个表达式的类型或者一个函数的类型。
函数的类型包括函数返回值,参数。
std::invoke_result_t---获取一个函数返回值的类型。
using priority_t = std::int_least16_t;
这行代码定义了一个类型别名`priority_t`,它表示一个`std::int_least16_t`类型。`std::int_least16_t`是C++标准库中定义的整型类型,表示至少包含16位的带符号整数类型。使用这个类型可以保证变量至少具有指定的位数,从而确保程序在不同平台上的可移植性。例如,如果在某些平台上`short int`类型只有8位(而不是16位),可以使用`std::int_least16_t`来代替`short int`类型,从而避免在不同平台上产生不同的结果。
std::promise<T>----std::future<T>
`std::promise`是C++11标准库中提供的一种同步机制,用于在线程之间共享数据。它通常与`std::future`一起使用,以便在异步操作完成时共享结果。
`std::promise`表示一个可以保存某个类型的值或异常的容器,可以通过调用`set_value`和`set_exception`方法将值或异常传递给与其关联的`std::future`对象。当`std::promise`的值被设置后,可以通过调用`get_future`方法获取一个`std::future`对象,进而获取`std::promise`的值或异常。
std::is_void_v<R>
`std::is_void_v<R>`是一个模板元编程工具,用于检查类型`R`是否是`void`类型的并返回一个布尔值(`true`或`false`)。因此,`std::is_void_v<R>`是一个编译时常量表达式,其值在编译期间确定。
在C++中,类型`void`表示“没有类型”,通常用于表示函数无返回值或指针不引用任何特定类型。当使用模板时,可能需要检查某个类型是否是`void`类型,以根据不同的类型做出不同的决策。这时就可以使用`std::is_void_v<R>`工具来完成这个任务。
class thread_pool
任务--task
/**
* @brief A queue of tasks to be executed by the threads.
*/
#ifdef BS_THREAD_POOL_ENABLE_PRIORITY
std::priority_queue<pr_task> tasks = {};
#else
std::queue<std::function<void()>> tasks = {};
#endif
线程池需要执行的任务被放入一个队列或者优先队列中。
新增任务和任务结束的条件变量
/**
* @brief A condition variable to notify worker() that a new task has become available.
*/
std::condition_variable task_available_cv = {};
/**
* @brief A condition variable to notify wait() that the tasks are done.
*/
std::condition_variable tasks_done_cv = {};
task_available_cv
tasks_done_cv
构造函数
提供给用户的thread_pool类有多构造方式。
但是thread_pool中的真正的thread_pool成员使用thread_pool这个构造函数,用户没有指定则使用默认值。
真正的thread_pool第一个构造参数:线程池;
第二个参数:每一个线程执行之前的初始化函数。
1,无参:默认线程数量===CPU核心数;
2,仅指定线程数量;
3,指定线程数量和线程初始化函数。
成员对象
thread_count----线程池中线程的数量;
threads-----------线程池,jthread对象数组;
std::unique_ptr<std::jthread[]> threads = nullptr;
成员函数
submit_task(task)
task是线程池中线程需要执行的函数,传递函数地址。
这个函数返回一个future对象。
template <typename F, typename R = std::invoke_result_t<std::decay_t<F>>>
[[nodiscard]] std::future<R> submit_task(F&& task BS_THREAD_POOL_PRIORITY_INPUT)
{
const std::shared_ptr<std::promise<R>> task_promise = std::make_shared<std::promise<R>>();
detach_task(
[task = std::forward<F>(task), task_promise]
{
try
{
if constexpr (std::is_void_v<R>)
{
task();
task_promise->set_value();
}
else
{
task_promise->set_value(task());
}
}
catch (...)
{
try
{
task_promise->set_exception(std::current_exception());
}
catch (...)
{
}
}
} BS_THREAD_POOL_PRIORITY_OUTPUT);
return task_promise->get_future();
}
future对象是什么?
submit_task处于主线程,这个接口怎么返回std::future对象?
submit_task中使用detach_task启动一个子线程。
detah_task(Func)
template <typename F>
void detach_task(F&& task BS_THREAD_POOL_PRIORITY_INPUT)
{
{
const std::scoped_lock tasks_lock(tasks_mutex);
tasks.emplace(std::forward<F>(task) BS_THREAD_POOL_PRIORITY_OUTPUT);
}
task_available_cv.notify_one();
}
std::scoped_lock
detack_task负责将任务加入任务队列中。
并通过条件变量task_available_cv唤醒一个线程-----notify_one()。
线程池中的所有线程必定是创建之后利用条件变量阻塞等待着,等待着被唤醒之后从task任务队列中获取任务执行。
几乎所有线程池实现的逻辑都是:线创建线程,利用信号量或者条件变量阻塞线程,信号量或者条件变量启动线程之后从此那个队列获取任务执行。
wait()
无参,
wait_for(_rel)
wait_until(_abs)
包含三个函数的实现,均用于future对象的等待操作。
第一个函数wait()表示等待future对象完成,默认情况下是一直等待,直到完成为止。它通过调用状态对象的wait()函数来实现等待操作。
第二个函数wait_for()表示等待future对象完成,最长等待一定时间,如果在指定的时间内完成了,则返回future_status::ready,否则返回future_status::timeout。它使用了C++基础库中的chrono库,来表示等待的时间长度。
第三个函数wait_until()表示等待future对象完成,直到指定的时间点,如果在指定时间点之前完成了,则返回future_status::ready,否则返回future_status::timeout。它也使用了chrono库来表示时间点。
thread_pool怎么从队列去除任务执行
thread+pool将任务加入task队列,那么什么时候,怎么样从队列中获取任务并执行呢?
thread_pool创建多个线程
这些线程会同时执行一个传入的任务吗,还是只是一个线程在执行任务?
jthread
jthread是一种用于跨线程通信和同步的C++20线程库,如果编译器不支持C++20,就不能使用它。
如果不指定使用C++20,报错:
所以编译项目必须指定使用C++20:
cmake_minimum_required(VERSION 3.22.1)
project(THREAD_POOL)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
add_executable(exc main.cpp timer.cpp)