c++ - 将C++条件变量与原子谓词一起使用,但不使用互斥

标签 c++ multithreading c++11 std condition-variable

我有两个线程。一个线程充当计时器线程,需要在固定的时间间隔内将通知发送到另一个线程。我打算使用C++条件变量。 (关于如何在following link中使用C++条件变量及其陷阱和陷阱,有一篇不错的文章)
我有以下限制/条件:

  • 通知线程无需锁定到互斥锁
  • 通知的(或接收者)线程做了一些有用的部分,但没有关键部分
  • 当且仅当接收器线程正在执行有用的工作时,它才可以错过通知。
  • 应该没有虚假的唤醒。

  • 使用上面的链接作为指导,我整理了以下代码
    // conditionVariableAtomic.cpp
    
    #include <atomic>
    #include <condition_variable>
    #include <iostream>
    #include <thread>
    #include <iostream>       // std::cout, std::endl
    #include <thread>         // std::this_thread::sleep_for
    #include <chrono>         // std::chrono::seconds
    
    
    std::mutex mutex_;
    std::condition_variable condVar;
    
    std::atomic<bool> dataReady{false};
    
    void waitingForWork(){
        int i = 0;
        while (i++ < 10)
        {
            std::cout << "Waiting " << std::endl;
            {
                std::unique_lock<std::mutex> lck(mutex_);
                condVar.wait(lck, []{ return dataReady.load(); });   // (1)
                dataReady = false;
            }
            std::cout << "Running " << std::endl;
            // Do useful work but no critical section.
        }
    }
    
    void setDataReady(){
        int i = 0;
        while (i++ < 10)
        {
            std::this_thread::sleep_for (std::chrono::seconds(1));
            dataReady = true;
            std::cout << "Data prepared" << std::endl;
            condVar.notify_one();
        }
    }
    
    int main(){
        
      std::cout << std::endl;
    
      std::thread t1(waitingForWork);
      std::thread t2(setDataReady);
    
      t1.join();
      t2.join();
      
      std::cout << std::endl;
      
    }
    
    我使用原子谓词来避免虚假唤醒,但不要在通知线程中使用lock_guard
    我的问题是:
  • 以上代码是否满足上面列出的约束/条件?
  • 我了解接收器线程无法避免互斥,因此需要在接收器中使用std::unique_lock<std::mutex> lck(mutex_);。但是我限制了std::unique_lock<std::mutex> lck(mutex_);的范围,即将以下代码部分
    std::unique_lock<std::mutex> lck(mutex_);
    condVar.wait(lck, []{ return dataReady.load(); });   // (1)
    dataReady = false;
    
    在范围块(也称为{ .... })内,这样互斥量在等待条件结束后就被解锁(接收器随后进行了一些有用的工作,但是由于没有关键部分,因此不需要在整个时间内都保持互斥量循环)。在这种情况下,这种有限的范围界定是否还会产生后果/副作用?还是需要在整个while循环中锁定unique_lock<std::mutex>
  • 最佳答案

    您的代码具有竞争条件。在检查dataReady谓词中的wait值和实际开始等待之间,另一个线程可以设置dataReady并调用notify_one。在您的示例中,这并不重要,因为您只会错过一个通知,然后在下一个通知中唤醒一秒钟。
    另一个竞争条件是,您可以在一个线程中将dataReady设置为true,在另一个线程中将dataReady设置回false,然后在第一个线程中调用notify_one,这又将导致等待时间比预期的更长。
    设置dataReady并使用condition变量以避免这些竞争时,应该在两个线程中都持有互斥体。
    您可以通过使用原子计数器而不是 bool(boolean) 值来避免第二种竞争条件,在一个线程上将其递增,然后在另一个线程上递减,然后在谓词中检查其是否为非零。

    关于c++ - 将C++条件变量与原子谓词一起使用,但不使用互斥,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63986619/

    相关文章:

    c++ - minGW(使用 g++ 4.7.1)不编译简单的 C++11 代码(在 WinVista 下)

    c++ - std::move on std::string 是否保证 .c_str() 返回相同的结果?

    c++ - 通过与输入相同的boost图创建无向图

    c++ - 如何通过 val 从 std::deque 中删除函数指针?

    c - 有很多线程 WaitingForSingleObject() 会降低性能吗?

    multithreading - 为什么 bool 值需要是原子的?

    c++ - 默认参数模板与可变参数模板 : what is the last template parameter?

    c++ - 英特尔引脚 : analysis routine detects ah register instead of rsp (REG_STACK_PTR)

    c++ - 标准属性和 GNU "__attribute__"之间有什么区别?

    c++ - : non-blocking write, 假想的锁机制读取并失效