真正的代码更复杂但我认为我设法制作了一个mcve。
我试图执行以下操作:
我使用的代码如下,似乎有效
std::atomic_int which_thread_to_wake_up;
std::atomic_int threads_asleep;
threads_asleep.store(0);
std::atomic_bool ALL_THREADS_READY;
ALL_THREADS_READY.store(false);
int threads_num = .. // Number of threads
bool thread_has_finished = false;
std::mutex mtx;
std::condition_variable cv;
std::mutex mtx2;
std::condition_variable cv2;
auto threadFunction = [](int my_index) {
// some heavy workload here..
....
{
std::unique_lock<std::mutex> lck(mtx);
++threads_asleep;
cv.notify_all(); // Wake up any other thread that might be waiting
}
std::unique_lock<std::mutex> lck(mtx);
bool all_ready = ALL_THREADS_READY.load();
size_t index = which_thread_to_wake_up.load();
cv.wait(lck, [&]() {
all_ready = ALL_THREADS_READY.load();
index = which_thread_to_wake_up.load();
return all_ready && my_index == index;
});
// This thread was awaken for work!
.. do some more work that requires synchronization..
std::unique_lock<std::mutex> lck2(mtx2);
thread_has_finished = true;
cv2.notify_one(); // Signal to the main thread that I'm done
};
// launch all the threads..
std::vector<std::thread> ALL_THREADS;
for (int i = 0; i < threads_num; ++i)
ALL_THREADS.emplace_back(threadFunction, i);
// Now the main thread needs to wait for ALL the threads to finish their first phase and go to sleep
std::unique_lock<std::mutex> lck(mtx);
size_t how_many_threads_are_asleep = threads_asleep.load();
while (how_many_threads_are_asleep < threads_num) {
cv.wait(lck, [&]() {
how_many_threads_are_asleep = threads_asleep.load();
return how_many_threads_are_asleep == numThreads;
});
}
// At this point I'm sure ALL THREADS ARE ASLEEP!
// Wake them up one by one (there should only be ONE awake at any time before it finishes his computation)
for (int i = 0; i < threads_num; i++)
{
which_thread_to_wake_up.store(i);
cv.notify_all(); // (*) Wake them all up to check if they're the chosen one
std::unique_lock<std::mutex> lck2(mtx2);
cv2.wait(lck, [&]() { return thread_has_finished; }); // Wait for the chosen one to finish
thread_has_finished = false;
}
我担心上一次notify_all()
电话(我用(*)标记的电话)可能会导致以下情况:
notify_all()
notify_all()
并且这已经失去了(因为线程已全部被唤醒,它们还没有简单地检查原子)这会发生吗?如果notify_all()
的调用以某种方式缓冲或与实际检查条件变量的函数同步的顺序,我找不到core-js
的任何措辞。
答案 0 :(得分:0)
根据(notify_all)
上的文档notify_all只是继续线程的一半要求。条件陈述也必须是真实的。所以必须有一个交通警察设计为唤醒第一个,唤醒第二个,唤醒第三个。 notify函数告诉线程检查那个条件。
我的答案比特定代码更高,但我希望有所帮助。
答案 1 :(得分:0)
您考虑的情况可能会发生。如果在调用notify_all()
时唤醒您的工作线程(从属),那么它们可能会错过该信号。
防止出现这种情况的一种方法是在mtx
之前锁定cv.notify_all()
并在之后将其解锁。正如wait()
文档中所建议的那样,lock用作pred()
访问的保护。如果主线程获取mtx
,则没有其他线程在同一时刻检查条件。虽然他们当时可能正在从事其他工作,但在您的代码中他们不太可能再次输入wait
。