我在一个用C++11编写的分布式作业系统中实现了一个栅栏(即在工作线程池之外的线程可能会请求阻塞,直到当前所有已安排的作业都完成),使用以下结构:
struct fence
{
std::atomic<size_t> counter;
std::mutex resume_mutex;
std::condition_variable resume;
fence(size_t num_threads)
: counter(num_threads)
{}
};
实现围栏功能的代码如下:
void task_pool::fence_impl(void *arg)
{
auto f = (fence *)arg;
if (--f->counter == 0) // (1)
// we have zeroed this fence's counter, wake up everyone that waits
f->resume.notify_all(); // (2)
else
{
unique_lock<mutex> lock(f->resume_mutex);
f->resume.wait(lock); // (3)
}
}
如果线程在一段时间内进入栅栏,那么这个方法会非常有效。然而,如果它们几乎同时尝试这样做,有时候会出现在原子递减(1)和开始等待条件变量(3)之间,线程会让出CPU时间并且另一个线程将计数器递减到零(1),并触发条件变量(2)。这将导致前一个线程在(3)中永远等待,因为它在收到通知后开始等待。
使它可行的方法是在(2)之前加上10毫秒的延迟,但由于明显的原因,这是不可接受的。
您有什么关于如何以高效的方式修复这个问题的建议吗?
typedef
(fence
是一个嵌套结构体),但我已经添加了模板参数以提高可读性,谢谢。 - IneQuationwhile(f->counter)
语句? - IneQuation