英文:
Can std::condition_variable::notify_one and wait_for occurr at the same time?
问题
It seems you want a translation of your code comments and questions. Here's the translated code with comments:
std::mutex m;
std::condition_variable cv;
int task = 0;
void worker_thread2()
{
{
// std::lock_guard<std::mutex> lk(m);
task++;
}
cv.notify_all();
LOG("notify_all");
}
int main()
{
std::thread worker(worker_thread2);
{
std::unique_lock<std::mutex> lk(m);
cv.wait_for(lk, std::chrono::milliseconds(5000), [&]{
if (task == 1) {
LOG("1111111");
return true;
} else {
LOG("0000000");
return false;
}
});
}
std::string m = "Back in main(), data = " + std::to_string(task);
LOG(m);
worker.join();
}
I have translated the code comments and left the code as is. If you have any specific questions about the code or its behavior, please let me know.
英文:
like my code, and i learn in https://en.cppreference.com/w/cpp/thread/condition_variable, my question is why i remove lock_guard with task++; then result is wait timeout. i learned, wait check task is 0, then unlock and to wait, but before wait, thread2 run finish task++ and notify, so cause Lost wake-up problem?
so i think should to lock task++ and cv.notify_all(); otherwise, Unable to solve Lost wake-up problem? but in cppreference example, unlock before notify_all, to avoid the waiting thread only to block again.
unlock+wait, wakeup, and lock
wait_for is lock , check, wait, unlock?
recv notify is wakeup + lock check exit?
std::mutex m;
std::condition_variable cv;
int task = 0;
void worker_thread2()
{
{
// std::lock_guard<std::mutex> lk(m);
task++;
}
cv.notify_all();
LOG("notify_all");
}
int main()
{
std::thread worker(worker_thread2);
{
std::unique_lock<std::mutex> lk(m);
cv.wait_for(lk, std::chrono::milliseconds(5000), [&]{
if (task == 1) {
LOG("1111111");
return true;
} else {
LOG("0000000");
return false;
}
});
}
std::string m = "Back in main(), data = " + std::to_string(task);
LOG(m);
worker.join();
}
D:\code\c++11\condition_variable\cmake-build-debug\condition_variable.exe
2023-6-8 20:37:15.208 00000002023-6-8 20:37:15.208 notify_all
2023-6-8 20:37:20.210 1111111
2023-6-8 20:37:20.212 Back in main(), data = 1
Process finished with exit code 0
答案1
得分: 1
If I am correct in thinking your main question is "Why do I need std::lock_guard<std::mutex> lk(m);
in worker_thread2
" it is because of this requirement listed on the cppreference page you link:
Even if the shared variable is atomic, it must be modified while owning the mutex to correctly publish the modification to the waiting thread.
If you want more details as to exactly why, you can see this answer which explains in more technical detail and links to some other answers.
If you are asking why you are seeing 0000000
printed before 1111111
, it is because the condition variable is working as intended. The main thread is locking the mutex before the worker thread has a chance to spawn, so the main thread checks if the predicate (task
) is the required value (1
in this case) to continue. Since it is not, it drops ownership of the lock (which is why a unique_lock
is required) allowing the worker thread to lock the mutex and modify task
, at which point the main thread is notified, wakes up, relocks the mutex, and sees that the task
is now the required value.
You can see from this example where I have added delay to your code that if the worker thread locks the mutex first, the main thread does not have a reason to check the predicate before waiting, and in fact, it cannot. (You may need to modify sleep_usec(10);
up or down a bit to see what I mean)
Output:
$ ./a.out
notify_all
1111111
Back in main(), data = 1
英文:
If I am correct in thinking your main question is "Why do I need std::lock_guard<std::mutex> lk(m);
in worker_thread2
" it is because of this requirement listed on the cppreference page you link:
> Even if the shared variable is atomic, it must be modified while owning the mutex to correctly publish the modification to the waiting thread.
If you want more details as to exactly why, you can see this answer which explains in more technical detail and links to some other answers.
If you are asking why you are seeing 0000000
printed before 1111111
, it is because the condition variable is working as intended. The main thread is locking the mutex before the worker thread has a chance to spawn, so the main thread checks if the predicate (task
) is the required value (1
in this case) to continue. Since it is not, it drops ownership of the lock (which is why a unique_lock
is required) allowing the worker thread to lock the mutex and modify task
, at which point the main thread is notified, wakes up, relocks the mutex, and sees that the task
is now the required value.
You can see from this example where I have added delay to your code that if the worker thread locks the mutex first, the main thread does not have a reason to check the predicate before waiting, and in fact, it cannot. (You may need to modify sleep_usec(10);
up or down a bit to see what I mean)
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
#define LOG(x) std::cout << x
// Portable usleep() analogue
template <typename T>
void sleep_usec(T duration){
std::this_thread::sleep_for(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::duration<T, std::micro>(duration)));
}
std::mutex m;
std::condition_variable cv;
int task = 0;
void worker_thread2(){
{
std::lock_guard<std::mutex> lk(m);
task++;
sleep_usec(100000);
}
cv.notify_all();
LOG("notify_all\n");
}
int main(){
std::thread worker(worker_thread2);
sleep_usec(10);
{
std::unique_lock<std::mutex> lk(m);
cv.wait_for(lk, std::chrono::milliseconds(5000), [&]{
if (task == 1){
LOG("1111111\n");
return true;
} else{
LOG("0000000\n");
return false;
}
});
}
std::string m = "Back in main(), data = " + std::to_string(task) + "\n";
LOG(m);
worker.join();
}
Output:
$ ./a.out
notify_all
1111111
Back in main(), data = 1
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论