线程池--thread_pool解析

国外的线程池库;

作者: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)

future

  • 18
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值