【发布时间】:2020-07-26 17:04:11
【问题描述】:
在带有 STL 线程的现代 C++ 中,我希望有两个工作线程轮流工作。一次只能有一个人工作,每个人只能在另一个人转身之前转一圈。我有这部分工作。 增加的约束是一个线程需要在另一个线程完成后继续轮流。但是在我的代码中,剩余的工作线程在第一个工作线程完成后会死锁。我不明白为什么,因为第一个工人做的最后一件事是解锁并通知条件变量,这应该唤醒第二个。代码如下:
{
std::mutex mu;
std::condition_variable cv;
int turn = 0;
auto thread_func = [&](int tid, int iters) {
std::unique_lock<std::mutex> lk(mu);
lk.unlock();
for (int i = 0; i < iters; i++) {
lk.lock();
cv.wait(lk, [&] {return turn == tid; });
printf("tid=%d turn=%d i=%d/%d\n", tid, turn, i, iters);
fflush(stdout);
turn = !turn;
lk.unlock();
cv.notify_all();
}
};
auto th0 = std::thread(thread_func, 0, 20);
auto th1 = std::thread(thread_func, 1, 25); // Does more iterations
printf("Made the threads.\n");
fflush(stdout);
th0.join();
th1.join();
printf("Both joined.\n");
fflush(stdout);
}
我不知道这是否是我不了解 STL 线程中的并发性,还是我的代码中存在逻辑错误。请注意,SO 上有一个与此类似的问题,但第二个工人不必比第一个工人运行更长的时间。我现在找不到链接到它。提前感谢您的帮助。
【问题讨论】:
-
当两个线程使用同一个互斥锁进行同步时,唯一的保证是只有一个线程会同时锁定互斥锁。不能保证当线程 A 释放时,线程 B 会成功锁定。如果你想要这样的序列,那么你需要自己实现它。这意味着两个线程必须合作以跟踪哪个线程最后拥有锁,并且每个线程必须检查哪个线程最后拥有锁,如果另一个线程仍在运行并且没有最近的锁,则立即释放。跨度>
-
谢谢。不过,我想知道,如果不能保证线程可以成功锁定,那么在任何使用互斥锁的代码中如何确保向前进展?有人可以使用互斥锁编写单线程代码,并且在获取互斥锁时可能会死锁。另外,如果不唤醒线程并导致它们开始执行,notify_all 是什么意思?
-
在线程(代码中的)设计、它们如何共享数据以及它们如何同步时需要解决这个问题。当一个线程释放一个互斥锁时,另一个尝试抓取(或随后尝试抓取)该互斥锁的线程很有可能会成功。但是不能保证哪个线程首先成功。实际上,如果您的代码依赖于特定的顺序,最好将相关的代码部分放在一个线程中,并按顺序执行它们(例如,执行 A 然后 B 并重复的循环)。
-
那种锁步执行意味着你真的没有使用线程。
-
也许这意味着我没有没有使用线程,但你可以清楚地看到我正在使用线程。我在这里建模的情况实际上是由于其他原因存在两个工作线程,它们在代码的不同部分执行不同的操作。然后有一段时间他们聚在一起,必须做这个锁步工作。在锁定步骤期间,我同意更明显的实现是先做 A 然后 B,然后重复。但这不适用于两个线程的更广泛的上下文。
标签: c++ multithreading stl