我的障碍同步方案是:
一个。线程在同步中向前迈出一步 - 即它们完成一个任务单元然后等待所有其他线程执行相同的操作,然后当它们执行时,它们可以再次向前移动。
湾我有大量的线程(大约256个)在条件变量上等待。满足条件时,将发送notify_all()
。
℃。 (出现饥饿风险的部分):在调用notify_all()
之前,runnable
变量设置为true
,计数器 - completed
- 设置为零。当线程完成其任务单元时,它会调用一个函数,该函数首先将runnable
设置为false
,然后递增completed
变量 - 当completed
等于阈值时满足条件(即需要完成任务单元的线程数),其中runnable
设置为true。
即,我们这样等待:
cond.wait(lck, runnable == true);
并且有这个:
unique_lock<mutex> lck(runMut);
runnable = true;
cond.notify_all();
我担心的是,任何一个醒来的线程都可以完成它的任务,然后在链中的一个线程被唤醒之前调用wait()
函数。当这个“低级”线程测试runnable
时,它会发现它设置为false
,然后再回到睡眠状态。
是否有设计模式或其他方式可以解决这个问题?
答案 0 :(得分:2)
您可以使用两个条件变量。一个通知工人开始工作,一个通知工人已完成工作。即你的主线程看起来像这样:
for (;;) {
running = 0;
done = 0;
cond_run.notify_all();
cond_done.wait(lck, []() { return done == threads; });
// handle results
}
你的工作线程看起来像这样:
for (;;) {
cond_run.wait(lck, []() { return running < threads; });
++running;
lck.unlock();
// do work
lck.lock():
++done;
cond_done.notify_one();
}
答案 1 :(得分:0)
并发很难。有很多方法可以解决这个问题,但真正重要的是让自己很容易确保它做对了。在这种特殊情况下,使用布尔值“runnable”来指示线程何时可以开始运行是令人困惑的。
相反,我会使用int currentStage指示每个人应该执行的阶段,并使用int numComplete来指示已完成currentStage的线程数。确保只在锁定状态下访问状态并且只进行有效转换 - 将numComplete增加到256的线程也应该将其重置为0并在释放锁定之前增加currentStage。
确保在对该状态进行任何更改后触发notify_all,然后很容易确保每个线程都做正确的事情。
答案 2 :(得分:0)
我实际上找不到一种让它工作的方法 - 问题是上面建议的两个变量解决方案没有处理虚假的唤醒,所以我选择通过不打扰谓词和生活来简化这个有关虚假唤醒的问题。