多个线程可以等待同一个条件变量吗?
Can multiple threads wait on the same condition variable?
我正在尝试了解如何更好地使用条件变量,并且我有以下代码。
行为。
代码的预期行为是:
- 每个线程打印“thread n waiting”
- 程序一直等到用户按下回车
- 当用户按下回车键时,notify_one为每个线程调用一次
- 所有线程打印“thread n ready.”,然后退出
代码的观察到的行为是:
- 每个线程打印“thread n waiting” (预期)
- 程序一直等到用户按下回车(预期)
- 当用户按下回车键时,notify_one 为每个线程调用一次 (预期)
- 其中一个线程打印“thread n ready”,但随后代码挂起。 (???)
问题。
为什么代码挂了?我怎样才能让多个线程等待同一个条件变量?
代码
#include <condition_variable>
#include <iostream>
#include <string>
#include <vector>
#include <thread>
int main() {
using namespace std::literals::string_literals;
auto m = std::mutex();
auto lock = std::unique_lock(m);
auto cv = std::condition_variable();
auto wait_then_print =[&](int id) {
return [&, id]() {
auto id_str = std::to_string(id);
std::cout << ("thread " + id_str + " waiting.\n");
cv.wait(lock);
// If I add this line in, the code gives me a system error:
// lock.unlock();
std::cout << ("thread " + id_str + " ready.\n");
};
};
auto threads = std::vector<std::thread>(16);
int counter = 0;
for(auto& t : threads)
t = std::thread(wait_then_print(counter++));
std::cout << "Press enter to continue.\n";
std::getchar();
for(int i = 0; i < counter; i++) {
cv.notify_one();
std::cout << "Notified one.\n";
}
for(auto& t : threads)
t.join();
}
输出
thread 1 waiting.
thread 0 waiting.
thread 2 waiting.
thread 3 waiting.
thread 4 waiting.
thread 5 waiting.
thread 6 waiting.
thread 7 waiting.
thread 8 waiting.
thread 9 waiting.
thread 11 waiting.
thread 10 waiting.
thread 12 waiting.
thread 13 waiting.
thread 14 waiting.
thread 15 waiting.
Press enter to continue.
Notified one.
Notified one.
thread 1 ready.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
这是未定义的行为。
为了等待条件变量,条件变量必须由最初锁定互斥体的同一个线程等待.您不能在一个执行线程中锁定互斥锁,然后在另一个线程中等待条件变量。
auto lock = std::unique_lock(m);
这个锁是在主执行线程中获取的。之后,主执行线程创建所有这些多个执行线程。这些执行线程中的每一个都执行以下操作:
cv.wait(lock)
此处调用wait()
的执行线程未获取互斥锁,因此这是未定义的行为。
仔细查看您在此处尝试执行的操作表明,如果您简单地移动,您可能会得到预期的结果
auto lock = std::unique_lock(m);
在每个新执行线程执行的 lambda 中。
由于各种竞争条件,您还需要简单地使用 notify_all()
而不是多次调用 notify_one()
。请记住,wait()
自动解锁互斥量并等待条件变量,而 wait()
returns 只有在线程被条件变量通知后成功重新锁定互斥量后。
我正在尝试了解如何更好地使用条件变量,并且我有以下代码。
行为。
代码的预期行为是:
- 每个线程打印“thread n waiting”
- 程序一直等到用户按下回车
- 当用户按下回车键时,notify_one为每个线程调用一次
- 所有线程打印“thread n ready.”,然后退出
代码的观察到的行为是:
- 每个线程打印“thread n waiting” (预期)
- 程序一直等到用户按下回车(预期)
- 当用户按下回车键时,notify_one 为每个线程调用一次 (预期)
- 其中一个线程打印“thread n ready”,但随后代码挂起。 (???)
问题。
为什么代码挂了?我怎样才能让多个线程等待同一个条件变量?
代码
#include <condition_variable>
#include <iostream>
#include <string>
#include <vector>
#include <thread>
int main() {
using namespace std::literals::string_literals;
auto m = std::mutex();
auto lock = std::unique_lock(m);
auto cv = std::condition_variable();
auto wait_then_print =[&](int id) {
return [&, id]() {
auto id_str = std::to_string(id);
std::cout << ("thread " + id_str + " waiting.\n");
cv.wait(lock);
// If I add this line in, the code gives me a system error:
// lock.unlock();
std::cout << ("thread " + id_str + " ready.\n");
};
};
auto threads = std::vector<std::thread>(16);
int counter = 0;
for(auto& t : threads)
t = std::thread(wait_then_print(counter++));
std::cout << "Press enter to continue.\n";
std::getchar();
for(int i = 0; i < counter; i++) {
cv.notify_one();
std::cout << "Notified one.\n";
}
for(auto& t : threads)
t.join();
}
输出
thread 1 waiting.
thread 0 waiting.
thread 2 waiting.
thread 3 waiting.
thread 4 waiting.
thread 5 waiting.
thread 6 waiting.
thread 7 waiting.
thread 8 waiting.
thread 9 waiting.
thread 11 waiting.
thread 10 waiting.
thread 12 waiting.
thread 13 waiting.
thread 14 waiting.
thread 15 waiting.
Press enter to continue.
Notified one.
Notified one.
thread 1 ready.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
这是未定义的行为。
为了等待条件变量,条件变量必须由最初锁定互斥体的同一个线程等待.您不能在一个执行线程中锁定互斥锁,然后在另一个线程中等待条件变量。
auto lock = std::unique_lock(m);
这个锁是在主执行线程中获取的。之后,主执行线程创建所有这些多个执行线程。这些执行线程中的每一个都执行以下操作:
cv.wait(lock)
此处调用wait()
的执行线程未获取互斥锁,因此这是未定义的行为。
仔细查看您在此处尝试执行的操作表明,如果您简单地移动,您可能会得到预期的结果
auto lock = std::unique_lock(m);
在每个新执行线程执行的 lambda 中。
由于各种竞争条件,您还需要简单地使用 notify_all()
而不是多次调用 notify_one()
。请记住,wait()
自动解锁互斥量并等待条件变量,而 wait()
returns 只有在线程被条件变量通知后成功重新锁定互斥量后。