使用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_forstd::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"。我可能更喜欢第一个基于旋转的解决方案,因为可以预期,等待时间会很短(如果系统没有超额订阅等)。