使用std::condition_variable时,如何降低生产者获得锁而消费者无法获得锁的可能性?
How to decrese the possibility of the producer acquiring the lock whereas the consumer could not get the lock when using std::condition_variable?
如何降低生产者(即下面代码片段中的主线程)获得锁而消费者(即等待线程)无法获得锁的可能性?如果你能告诉我一种避免它的方法会更好。我不认为使用 std::thread::sleep_for
或 std::thread::yield
是个好主意。而且我做了一些测试,发现使用std::thread::yield
时没有效果。
这个问题我想了很久,希望大家帮忙解答一下。
如果你运行代码片段,你可能会看到这样的输出:
Waiting...
test
Notifying falsely...
Notifying true change...
Notifying true change...
Notifying true change...
Notifying true change...
Notifying true change...
Notifying true change...
(**many many such output**)
Notifying true change...
test
...finished waiting. i == 1
这是相关的代码片段(检查https://godbolt.org/z/9dwDJN,引用自en.cppreference。com/w/cpp/thread/condition_variable/notify_one):
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
std::condition_variable cv;
std::mutex cv_m;
int i = 0;
bool done = false;
void waits()
{
std::unique_lock<std::mutex> lk(cv_m);
std::cout << "Waiting... \n";
cv.wait(lk, []{std::cout<<"test"<<std::endl; return i == 1;});
std::cout << "...finished waiting. i == 1\n";
done = true;
}
void signals()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Notifying falsely...\n";
cv.notify_one(); // waiting thread is notified with i == 0.
// cv.wait wakes up, checks i, and goes back to waiting
std::unique_lock<std::mutex> lk(cv_m);
i = 1;
while (!done)
{
std::cout << "Notifying true change...\n";
lk.unlock();
cv.notify_one(); // waiting thread is notified with i == 1, cv.wait returns
//std::this_thread::sleep_for(std::chrono::seconds(1)); // I don't think it is good method.
//std::this_thread::yield(); //Maybe, this does not work.
lk.lock();
}
}
int main()
{
std::thread t1(waits), t2(signals);
t1.join();
t2.join();
}
为什么您认为使用睡眠是个坏主意?
使用 std::this_thread::sleep_for(std::chrono::milliseconds(100)) 是相当标准的;在 while(true) 线程中,它也将解决您的问题。
如果您将其设为 atomic 变量,您可以完全等待 done
而无需锁定。在这种情况下,这对我来说甚至比使用互斥锁更有意义。但是,这并没有改变busy waiting的整个概念,即一直循环直到设置done
。
或者,您可以等待 done
设置而不阻塞 CPU 核心。只需使用相同的条件变量概念。您甚至可以使用用于 i
同步的相同条件变量。这种方法的演示在这里:https://en.cppreference.com/w/cpp/thread/condition_variable.
问题是这两个解中的哪一个是"better"。我可能更喜欢第一个基于旋转的解决方案,因为可以预期,等待时间会很短(如果系统没有超额订阅等)。
如何降低生产者(即下面代码片段中的主线程)获得锁而消费者(即等待线程)无法获得锁的可能性?如果你能告诉我一种避免它的方法会更好。我不认为使用 std::thread::sleep_for
或 std::thread::yield
是个好主意。而且我做了一些测试,发现使用std::thread::yield
时没有效果。
这个问题我想了很久,希望大家帮忙解答一下。
如果你运行代码片段,你可能会看到这样的输出:
Waiting...
test
Notifying falsely...
Notifying true change...
Notifying true change...
Notifying true change...
Notifying true change...
Notifying true change...
Notifying true change...
(**many many such output**)
Notifying true change...
test
...finished waiting. i == 1
这是相关的代码片段(检查https://godbolt.org/z/9dwDJN,引用自en.cppreference。com/w/cpp/thread/condition_variable/notify_one):
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
std::condition_variable cv;
std::mutex cv_m;
int i = 0;
bool done = false;
void waits()
{
std::unique_lock<std::mutex> lk(cv_m);
std::cout << "Waiting... \n";
cv.wait(lk, []{std::cout<<"test"<<std::endl; return i == 1;});
std::cout << "...finished waiting. i == 1\n";
done = true;
}
void signals()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Notifying falsely...\n";
cv.notify_one(); // waiting thread is notified with i == 0.
// cv.wait wakes up, checks i, and goes back to waiting
std::unique_lock<std::mutex> lk(cv_m);
i = 1;
while (!done)
{
std::cout << "Notifying true change...\n";
lk.unlock();
cv.notify_one(); // waiting thread is notified with i == 1, cv.wait returns
//std::this_thread::sleep_for(std::chrono::seconds(1)); // I don't think it is good method.
//std::this_thread::yield(); //Maybe, this does not work.
lk.lock();
}
}
int main()
{
std::thread t1(waits), t2(signals);
t1.join();
t2.join();
}
为什么您认为使用睡眠是个坏主意? 使用 std::this_thread::sleep_for(std::chrono::milliseconds(100)) 是相当标准的;在 while(true) 线程中,它也将解决您的问题。
如果您将其设为 atomic 变量,您可以完全等待 done
而无需锁定。在这种情况下,这对我来说甚至比使用互斥锁更有意义。但是,这并没有改变busy waiting的整个概念,即一直循环直到设置done
。
或者,您可以等待 done
设置而不阻塞 CPU 核心。只需使用相同的条件变量概念。您甚至可以使用用于 i
同步的相同条件变量。这种方法的演示在这里:https://en.cppreference.com/w/cpp/thread/condition_variable.
问题是这两个解中的哪一个是"better"。我可能更喜欢第一个基于旋转的解决方案,因为可以预期,等待时间会很短(如果系统没有超额订阅等)。