2012-10-14 201 views
2

我需要一个用于我的应用程序的线程池,并且我希望尽可能地依赖标准(C++ 11或boost)内容。我意识到有一个非官方的(!)boost线程池类,它基本上解决了我需要的东西,但是我宁愿避免它,因为它不在boost库本身 - 为什么在它之后仍然不在核心库中很多年?boost线程池

在本页和其他地方的一些帖子中,人们建议使用boost :: asio来实现类似于线程池的行为。乍一看,这看起来像我想做的事情,但是我发现我看到的所有实现都无法加入当前活动的任务,这使得它对我的应用程序无用。为了执行一个连接,他们向所有的线程发送停止信号并随后加入它们。但是,这完全消除了我的用例中线程池的优点,因为这使得新任务需要创建一个新线程。

我想要做的是:

ThreadPool pool(4); 
for (...) 
{ 
    for (int i=0;i<something;i++) 
     pool.pushTask(...); 
    pool.join(); 
    // do something with the results 
} 

任何人都可以提出一个解决方案(除了使用SourceForge上的现有非官方的线程池)?在C++ 11或核心提升中有什么可以帮助我吗?

+0

你不想'boost :: threadpool'因为它是非官方的? –

回答

0

加入一个线程均值停止,直到它停止,如果停止并且您想要为其分配新任务,则必须创建一个新线程。所以在你的情况下,你应该等待一个条件(例如boost::condition_variable)来指示任务结束。所以使用这种技术很容易实现它使用boost::asioboost::condition_variable。每个线程调用boost::asio::io_service::run和任务将在不同的线程上进行调度和执行,并且在最后,每个任务将设置boost::condition_variable或事件递减std::atomic以指示作业结束!那真的很简单,不是吗?

+0

对不起,当我说加入,我通常意味着等待任务(而不是线程)的终止。但通常,是的,我同意条件变量是一个解决方案。但是,现在在我的外循环的每次迭代中,我需要实例化条件变量。我不确定这是否是一项昂贵的操作?至于std :: atomic:我不知道父线程可以“等待”原子为零吗? – Dtag

+1

我只是说条件变量作为示例,但您甚至可以使用其他技术,例如使用互斥锁来访问计数变量,并且当计数达到零时设置条件变量并且当它递增时重置它。你已经知道解决方案! – BigBoss

1

让每个任务类都来自具有“OnCompletion(任务)”方法/事件的任务。线程池线程可以在调用任务的main run()方法之后调用它。

等待完成单个任务很容易。 OnCompletion()可以执行任何需要的信号来指示始发线程,发出condvar信号,将任务排队到生产者 - 消费者队列,调用SendMessage/PostMessage API,Invoke/BeginInvoke等等。

如果一个oringinating线程需要等待多个任务全部完成,那么可以扩展上面的内容并向池中发出一个“等待任务”。等待任务有其自己的OnCompletion来传达其他任务的完成,并有一个线程安全的“任务计数器”(原子操作或锁),设置为要发布的“主”任务的数量。等待任务首先发送到池,并且运行它的线程等待等待任务中的私有'allDone'condvar。然后将'主要'任务发布到池中,其OnCompletion设置为调用等待任务的方法,将任务计数器递减至零。当任务计数器达到零时,实现此目标的线程将发出allDone condvar信号。等待任务OnCompletion然后运行,并表示完成所有主要任务。

这样的机制不需要线程池线程的不断创建/终止/加入/删除,也不需要限制发送任务如何发送信号,并且可以根据需要发出任意数量的任务组。但是,您应该注意,每个等待任务都会阻塞一个线程池线程,因此请确保在池中创建一些额外的线程(通常不会有任何问题)。

1

这看起来像boost::futures的工作。文档中的示例似乎正好展示了您正在寻找的内容。

3

乍一看,这看起来像我想做的事情,但是我发现我看到的所有实现都无法加入当前活动任务,这使得它对我的应用程序无用。为了执行一个连接,他们向所有的线程发送停止信号并随后加入它们。但是,这完全消除了我的用例中线程池的优点,因为这使得新任务需要创建一个新线程。

我想你可能误会了ASIO例如:

IIRC(它已经有一段时间),每个线程的线程池运行呼吁io_service::run这意味着有效的每个线程都有一个事件循环和调度。然后让asio完成任务,使用io_service :: post方法将任务发布到io_service,并使用asio的调度机制负责其余部分。只要你不叫io_service::stop,线程池将继续运行,使用与你开始运行时一样多的线程(假设每个线程都有工作要做或已经被分配了一个io_service::work对象)。

所以你不需要需要为新任务创建新线程,这将违背线程池的概念。