本文主要是介绍thread_pool的一些想法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
之前有探讨过线程池的逻辑,这两天刚好闲下来,看了github上C++20的句法,语法是有点夸张,
以这个push为例:
template<typename F, typename... Args>requires std::invocable<F, Args...>
inline std::future<std::invoke_result_t<F, Args...>> push(F&& f, Args&&... args)
{// std::packaged_task is used to get a future out of the function call.auto pck = std::make_shared<std::packaged_task<std::invoke_result_t<F, Args...>()>>(// This has been tested to ensure perfect forwarding still occurs with// the parameters captured by reference.[&f, &args...]{if constexpr (sizeof...(args) == 0){// Only need to forward the function.return std::forward<F>(f);}else{// std::forward is used to ensure perfect// forwarding of rvalues where necessary.// std::bind is used to make a parameterless function// that simulates calling f with its respective arguments.return std::bind(std::forward<F>(f), std::forward<Args>(args)...);}}());// Lastly, create a function that wraps the packaged// task into a signature of void().this->tasks.push(std::make_unique<std::function<void()>>([pck] { (*pck)(); }));// Notify one waiting thread so it can wake up and take this new task.std::lock_guard lock(this->signal_mut);this->signal.notify_one();// Return the future, the user is now responsible// for the return value of the task.return pck->get_future();
}
这段代码是C++20标准中使用的概念(concepts)和协程(coroutines)的一部分,用于创建一个异步执行函数并返回其结果的std::future
。
-
概念定义(Concept Definition):
template<typename F, typename... Args> concept Invocable = std::invocable<F, Args...>;
这里定义了一个名为
Invocable
的概念。std::invocable
是一个C++标准库中的类型特征,用于检查给定的类型F
是否可以使用参数Args...
进行调用。如果F
可以被调用,那么Invocable
概念就是有效的。当前程序中没有自己定义concept,而是直接使用了std定义的invocable -
模板函数定义:
template<typename F, typename... Args> requires std::invocable<F, Args...> std::future<std::invoke_result_t<F, Args...>> push(F&& f, Args&&... args)
定义了一个模板函数
myFunction
,它接受一个可调用对象F
和任意数量的参数Args...
。函数的返回类型是一个std::future
,其模板参数std::invoke_result_t<F, Args...>
是F
调用Args...
后返回的类型。requires std::invocable<F, Args...>
是一个约束,它要求F
和Args...
必须满足std::invocable
概念,即F
必须能够被调用,并且其参数类型必须是Args...
。 -
创建
std::packaged_task
对象:
auto pck = std::make_shared<std::packaged_task<std::invoke_result_t<F, Args...>()>>(// This has been tested to ensure perfect forwarding still occurs with// the parameters captured by reference.[&f, &args...]{if constexpr (sizeof...(args) == 0){// Only need to forward the function.return std::forward<F>(f);}else{// std::forward is used to ensure perfect// forwarding of rvalues where necessary.// std::bind is used to make a parameterless function// that simulates calling f with its respective arguments.return std::bind(std::forward<F>(f), std::forward<Args>(args)...);}}());
这里创建了一个std::packaged_task
对象的共享指针pck
。std::packaged_task
是一个封装了可调用对象和其参数的类,它可以被用来生成std::future
对象。
构造函数中的lambda表达式是一个函数对象,它根据参数的数量来决定如何调用F
。如果Args...
为空,则直接返回std::forward<F>(f)
;否则,使用std::bind
来绑定f
和args...
。
4. 返回std::future
:
return pck->get_future();
最后,函数返回pck
对象的get_future()
方法的结果,这是一个std::future
对象,它将与std::packaged_task
对象关联,允许异步访问F
调用的结果。
整体逻辑:
就是把各种类型,各种参数的函数封装成对象,然后像普通数据一样传递到列表中,线程池按一定规则获取这些对象执行,完成后发起线程再获取返回数据。
应用:
有了这个东西,C++编写行情服务器的难度将由地狱级一下上升为玩具级,所有的数据处理函数放在队列中,然后线程池按规则将这些队列执行,将数据带给各自的发起处。这些队列中,有些是专门进行常规数据处理的,有些则是响应用户请求,还有的则是可能查看用户权限什么的,全部可以拆分开来扔到队列中,由线程池并行执行,若需要有先后顺序的,加上一定编号和优先级即可。
如此的行情服务器即规范又可以发挥出CPU的最大性能。
拓展:
线程池一般分两种,当前讨论的属于消费--生产者模式,即用户请求,数据处理这些都属于生产端,线程池则属于消费端,适合处理高并发,低数据传输的。若产生的数据非常巨大,则可以换一种线程池模型,这种模型有三种不同属性,同一时刻系统中只有一个领导者,由它来进行调度,他会先选择好继任者后进入工作者模式,并完成自己的工作,而原先的继任者则成为领导者,接替原先领导者的一切工作。好处是数据无需在线程中传递,难点是各线程状态的切换。后面有时间应想办法实现这个线程池模式,看起来这种线程池做行情服务器更加高效。
这篇关于thread_pool的一些想法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!