C++ 当使用std::condition_变量时,如何降低生产者获得锁而消费者无法获得锁的可能性?

C++ 当使用std::condition_变量时,如何降低生产者获得锁而消费者无法获得锁的可能性?,c++,c++11,conditional-statements,mutex,race-condition,C++,C++11,Conditional Statements,Mutex,Race Condition,如何降低生产者(即下面代码段代码中的主线程)获取锁而消费者(即等待线程)无法获取锁的可能性?如果你能告诉我一个避免它的方法会更好。我认为对或std::thread::yield使用std::thread::sleep\u不是一个好主意。我做了一些测试,发现使用std::thread::yield时没有效果 我已经考虑了很长时间,如果能在这个问题上得到一些帮助,我将不胜感激 如果运行代码段,您可能会看到这样的输出: Waiting... test Notifying falsely... N

如何降低生产者(即下面代码段代码中的主线程)获取锁而消费者(即等待线程)无法获取锁的可能性?如果你能告诉我一个避免它的方法会更好。我认为对或
std::thread::yield使用
std::thread::sleep\u不是一个好主意。我做了一些测试,发现使用
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
以下是相关的代码片段(检查,引用自en.cppreference.com/w/cpp/thread/condition\u variable/notify\u one):

#包括
#包括
#包括
#包括
std::条件变量cv;
std::mutex cv_m;
int i=0;
bool done=false;
void waits()
{
std::唯一锁定lk(cv_m);

为什么你认为睡眠是个坏主意?
使用std::this_thread::sleep_for(std::chrono::毫秒(100));in while(true)是非常标准的线程,它也会解决您的问题。

如果将其设置为原子变量,您可以完全不带锁地等待
完成。
在这种情况下,这比使用互斥锁更有意义。但是,这并不会改变忙等待的整个概念,即在设置
完成之前在循环中旋转

或者,您可以等待
done
设置,而不阻塞CPU核心。只需使用相同的条件变量概念。您甚至可以使用用于同步
i
的相同条件变量。此方法的演示如下:


问题是这两个解决方案中哪一个“更好”。我可能更喜欢第一个基于旋转的解决方案,因为人们可以预期,等待时间将非常短(如果系统没有超额订阅等)。

您的问题是由旋转(
while
循环)引起的。即使您将
done
更改为原子,这样可以避免在循环中锁定,即使这样,输出也不会更改。或者,您可以使用条件变量空闲等待设置
done
,示例如下:。@DanielLangr,好的,我已经删除了我的答案。现在我迷路了:即使旋转while循环无法在最后一条测试线之前多次生成通知真实更改。@DmitryKuzminov为什么不?您正在旋转,并且消息在每次迭代中都会打印出来。它将一直打印,直到信令线程通知更改
done
,而更改不会立即发生。请注意
notify\u one
d在等待线程得到通知之前,oes不会阻塞。它只是返回,不管等待线程是否得到真正的通知。@DanielLangr,信令线程通知一个服务员。等待线程作为唯一等待的线程获得互斥锁所有权。“测试”在信号线程获得锁的所有权之前,应该输出行,因此不应该有多个“通知真实更改…”@John,它实际上没有回答您的问题。谢谢您的澄清。我几乎理解您的意思。我已经阅读了您前面提到的文档。我现在理解如何实现它。但我仍然有一个问题,您所说的“或者,您可以等待完成设置而不阻塞CPU核心”是什么意思?你的意思是要避免CPU核心被繁忙的等待占据吗?这是一个文书错误吗?如果被阻止,我们通常认为某些东西是空闲的。我不是母语人士。也许,这只是我的误解。@John是的,没错,我的意思是要避免CPU核心被繁忙的等待占据。@Daniel Langer谢谢你的澄清。是吗“ABA问题”是一个术语“你有没有深入挖掘?”请你给我一些相关的文件,让我来看看。“约翰,对于ABA问题,请阅读下面的评论。对于一些文档,我不知道具体的。我可以使用谷歌,但是你也可以使用它。一般来说,关于C++中的多读,我会建议在动作BO中使用C++并发。k、 引用您的评论[我的重点]:这里是ABA问题的一个实时演示:godbolt.org/z/8z6WWO.*等待线程很可能不会读取1并永远运行。**我完全理解您的意思和代码片段。但是我如何解决这个ABA问题?您前面提到的方法(即en.cppreference.com/w/cpp/thread/condition_variable.)确实是解决此问题的一种方法。但在某些情况下,例如从外围设备读取数据(生产者等待消费者可能会导致某些数据丢失)并不适用。
#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();
}