【发布时间】:2018-04-03 05:17:47
【问题描述】:
我有以下代码,它在注释行上死锁。基本上 f1 和 f2 在程序中作为单独的线程运行。 f1 期望 i 为 1 并将其递减,通知 cv。 f2 期望 i 为 0 并增加它,通知 cv。我假设如果 f2 将 i 增加到 1,调用 cv.notify(),然后 f1 读取一个过时的 i 值(即 0),就会发生死锁,因为互斥锁和 i 之间没有内存同步,然后等待并且永远不会被唤醒向上。然后 f2 也进入睡眠状态,现在两个线程都在等待一个永远不会被通知的 cv。
我怎样才能编写这段代码,以免发生死锁?基本上我想要实现的是拥有一些由两个线程更新的原子状态。如果其中一个线程的状态不正确,我不想旋转;相反,我想在值正确时使用 cv 功能(或类似功能)来唤醒线程。
我是用g++-7用O3编译代码(虽然O0和O3都出现死锁)。
#include <atomic>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
std::atomic_size_t i{0};
std::mutex mut;
std::condition_variable cv;
void f1() {
while (1) {
{
std::unique_lock<std::mutex> lk(mut);
cv.wait(lk, []() { return i.load() > 0; }); // deadlocks
}
--i;
cv.notify_one();
std::cout << "i = " << i << std::endl; // Only to avoid optimization
}
}
void f2() {
while (1) {
{
std::unique_lock<std::mutex> lk(mut);
cv.wait(lk, []() { return i.load() < 1; }); // deadlocks
}
++i;
cv.notify_one();
std::cout << "i = " << i << std::endl; // Only to avoid optimization
}
}
int main() {
std::thread t1(f1);
std::thread t2(f2);
t1.join();
t2.join();
return 0;
}
编辑:cout 只是为了避免编译器优化。
【问题讨论】:
-
—i 和 ++I 应该在 while 内。否则永远不会达到条件
-
在值可能被另一个线程更改后打印它们。
-
我在猜测,但我认为在一个线程更改了值但 在它调用通知之后可能会发生虚假唤醒。因此,两个线程最终都可以等待变量,而不会再次调用 notify。将增量/减量移动到锁中似乎可以解决它。
-
虽然将其移入 while 是一种解决方案,但我想知道是否可以不进行双重同步(原子已经在同步 i)。
-
@user1413793 使
i不是原子的,那么你只需要依赖互斥锁进行同步。
标签: c++ multithreading atomic c++17 condition-variable