问题描述
我想创建一个用于实验目的(和有趣因素)的线程池.它应该能够处理各种各样的任务(因此我可以在以后的项目中使用它).
I want to create a thread pool for experimental purposes (and for the fun factor). It should be able to process a wide variety of tasks (so I can possibly use it in later projects).
在我的线程池类中,我将需要某种任务队列.由于标准库自C ++ 11标准以来提供了std::packaged_task
,因此我的队列看起来像std::deque<std::packaged_task<?()> > task_queue
,因此客户端可以通过某种公共接口函数将std::packaged_task
推入队列(然后使用池中的线程会收到条件变量通知执行该线程,等等.
In my thread pool class I'm going to need some sort of task queue. Since the Standard Library provides std::packaged_task
since the C++11 standard, my queue will look like std::deque<std::packaged_task<?()> > task_queue
, so the client can push std::packaged_task
s into the queue via some sort of public interface function (and then one of the threads in the pool will be notified with a condition variable to execute it, etc.).
我的问题与双端队列中std::packaged_task<?()>
的模板参数有关.
My question is related to the template argument of the std::packaged_task<?()>
s in the deque.
函数签名?()
应该能够处理任何类型/数量的参数,因为客户端可以执行以下操作:
The function signature ?()
should be able to deal with any type/number of parameters, because the client can do something like:
std::packaged_task<int()> t(std::bind(factorial, 342));thread_pool.add_task(t);
std::packaged_task<int()> t(std::bind(factorial, 342));thread_pool.add_task(t);
所以我不必处理参数的类型/数量.
So I don't have to deal with the type/number of parameters.
但是返回值应该是什么?(因此带有问号)
-
如果我将整个线程池类设为模板类,则为一个实例它只能处理具有特定签名的任务(如
std::packaged_task<int()>
).
If I make my whole thread pool class a template class, one instanceof it will only be able to deal with tasks with a specific signature(like
std::packaged_task<int()>
).
我希望一个线程池对象能够处理任何类型的任务.
如果我选择std::packaged_task<void()>
并调用了函数返回一个整数或任何形式的东西,然后就是未定义的行为.
If I go with std::packaged_task<void()>
and the function invokedreturns an integer, or anything at all, then thats undefined behaviour.
推荐答案
所以最困难的部分是packaged_task<R()>
仅可移动,否则您可以将其扔到std::function<void()>
中,然后在线程中运行它们.
So the hard part is that packaged_task<R()>
is move-only, otherwise you could just toss it into a std::function<void()>
, and run those in your threads.
有几种解决方法.
首先,荒谬的是,使用packaged_task<void()>
存储packaged_task<R()>
.我建议不要这样做,但是确实可以. ;)(packaged_task<R()>
上的operator()
的签名是什么?传递给packaged_task<void()>
的对象所需的签名是什么?)
First, ridiculously, use a packaged_task<void()>
to store a packaged_task<R()>
. I'd advise against this, but it does work. ;) (what is the signature of operator()
on packaged_task<R()>
? What is the required signature for the objects you pass to packaged_task<void()>
?)
第二,将packaged_task<R()>
包裹在shared_ptr
中,在带有签名void()
的lambda中捕获,将其存储在std::function<void()>
中,然后完成.这有间接费用,但可能比第一种解决方案要少.
Second, wrap your packaged_task<R()>
in a shared_ptr
, capture that in a lambda with signature void()
, store that in a std::function<void()>
, and done. This has overhead costs, but probably less than the first solution.
最后,编写您自己的仅移动函数包装器.对于签名void()
,它很短:
Finally, write your own move-only function wrapper. For the signature void()
it is short:
struct task {
template<class F,
class dF=std::decay_t<F>,
class=decltype( std::declval<dF&>()() )
>
task( F&& f ):
ptr(
new dF(std::forward<F>(f)),
[](void* ptr){ delete static_cast<dF*>(ptr); }
),
invoke([](void*ptr){
(*static_cast<dF*>(ptr))();
})
{}
void operator()()const{
invoke( ptr.get() );
}
task(task&&)=default;
task&operator=(task&&)=default;
task()=default;
~task()=default;
explicit operator bool()const{return static_cast<bool>(ptr);}
private:
std::unique_ptr<void, void(*)(void*)> ptr;
void(*invoke)(void*) = nullptr;
};
简单.上面可以存储任何类型R
的packaged_task<R()>
,并在以后调用它们.
and simple. The above can store packaged_task<R()>
for any type R
, and invoke them later.
这具有相对最小的开销-它应该比std::function
便宜,至少是我所见过的实现-除非它不做SBO(小缓冲区优化),它在内部存储小函数对象而不是在上面堆.
This has relatively minimal overhead -- it should be cheaper than std::function
, at least the implementations I've seen -- except it does not do SBO (small buffer optimization) where it stores small function objects internally instead of on the heap.
如果需要,可以通过对缓冲区进行小的优化来改进unique_ptr<> ptr
容器.
You can improve the unique_ptr<> ptr
container with a small buffer optimization if you want.
这篇关于在C ++ 11中实现一个简单的通用线程池的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!