为了解决问题(并且更好地理解多任务处理),我编写了一个小型线程池实现。此线程池会旋转许多工作线程,这些线程会在线程池的客户端添加任务时将任务从队列中弹出。出于此问题的目的,当任务队列为空时,工作线程全部终止。
在做了一些基本的基准测试后,我发现应用程序花费大约60%的时间等待获取队列锁定。据推测,这主要发生在工人线程中。
这仅仅表明我没有给予工作者足够的线程,或者更多的东西吗?是否有一些直截了当的东西可能会丢失以增加工作线程吞吐量?
编辑:这是一些粗略的伪代码,应该有所说明。这是在执行工作线程期间获取/释放锁的唯一两个地方(这是应用程序运行时间的绝大部分。)
std::list<task_t> task_list;
// Called by the client to add tasks to the thread pool
void insert_task(const task_t& task)
{
lock_type listlock(task_mutex);
task_list.push_back(task);
}
// The base routine of each thread in the pool. Some details
// such as lifetime management have been omitted for clarity.
void worker_thread_base()
{
while (true)
{
task_t task;
{
lock_type listlock(task_mutex);
if (task_list.empty())
continue;
task = task_list.front();
task_list.pop_front();
}
do_task(task);
}
}
答案 0 :(得分:0)
您是否尝试使用单个锁,多个锁来执行此操作? Mutexs?您使用什么等待语义?
我会从你的描述中猜测(这纯粹是猜测)你有类似的东西:
lock(theLock) {
// ... do lots of work ...
}
在主线程中包含要分派给轻量级线程的代码。您可能会在此看到聚合等待时间的一个原因是因为您需要从已经排队并正在等待执行的spun up线程返回信号(再次这是猜测,因为您没有给出任何代码)。
您可以解决此问题的一种方法是从使用显式锁定(如上所述)切换到使用已发出信号的互斥锁,当您希望其中一个线程获取工作时,该互斥锁是脉冲式的。
虽然没有看到你当前的实现,但我不确定我是否可以做得更多。
答案 1 :(得分:0)
您的设计是在每个线程所在的位置构建的,并且“旋转”以尝试获取锁定。除非每个工作线程都在执行工作,否则这种情况会不断发生 - 在这种情况下锁定将被取消并且工作将会发生。
当你的所有线程都坐着,旋转锁定时,你将花费相当多的CPU时间等待。考虑到您的设计,这有点预期。
如果你的工作线程较少,你会发现被阻止的时间百分比可能会急剧缩小 - 而且当你拥有比线程更多的工作项时,你将花费很少的时间等待这个锁。 / p>
更好的设计是为工作队列使用某种形式的无锁队列,因为这可能会阻止此时等待。此外,拥有一个等待句柄可以阻止工作线程直到队列中有工作,这将防止不必要的旋转。