本文介绍了在C ++ 11中实现一个简单的通用线程池的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个用于实验目的(和有趣因素)的线程池.它应该能够处理各种各样的任务(因此我可以在以后的项目中使用它).

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_tasks 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;
};

简单.上面可以存储任何类型Rpackaged_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中实现一个简单的通用线程池的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

06-16 02:19