代码之家  ›  专栏  ›  技术社区  ›  theateist

条件变量卡在等待中

  •  1
  • theateist  · 技术社区  · 6 年前

    如果在中通知条件变量,为什么它在等待时卡住 worker_thread ?我错过了什么?

    #include <thread>
    #include <mutex>
    #include <condition_variable>
    #include <iostream>
    
    std::mutex m;
    std::condition_variable cv;
    
    void worker_thread()
    {
        cv.notify_one();
    }
    
    int main()
    {
        std::thread worker(worker_thread);
    
        std::cout << "Start waiting..." << std::endl;
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk);
        std::cout << "Finished waiting..." << std::endl;
    
        worker.join();
    
        getchar();
    }
    
    3 回复  |  直到 6 年前
        1
  •  1
  •   1201ProgramAlarm    6 年前

    notify_one 如果有等待线程,将取消阻止该线程。如果没有等待线程,则不会发生任何事情。一 condition_variable 没有状态来记住等待时应通知多少线程。

        2
  •  1
  •   Yakk - Adam Nevraumont    6 年前

    你的问题是 cv.notify_one() 仅唤醒当前正在等待的线程。 cv 不记得你通知过,后来有人过来等着。

    你的工作线程超过了你的主线程。所以通知发生在主线程之前。

    这只是您真正问题的一个症状;您使用的条件变量是错误的。除非是非常高级的使用,否则条件变量的所有使用都应该是三重的。

    1. std::condition_variable .

    2. std::mutex .

    3. 有效载荷。

    您的代码缺少有效负载。

    为了发出信号,你:

    std::unique_lock<std::mutex> l(m);
    payload = /* move it to a 'set' or 'non-empty' state */;
    cv.notify_one(); // or all
    

    听你说:

    std::unique_lock<std::mutex> l(m);
    cv.wait(l, [&]{ return /* payload is in a set or non-empty state */; });
    // while locked, consume one "unit" of payload from the payload.
    

    有微小的变化 wait_for 等等。

    遵循这种货物崇拜模式很重要,因为它避免了许多陷阱。它处理两个虚假的唤醒和通知后的等待。

    您的代码缺少有效负载。因此,您的代码既易受等待线程超过信令线程的攻击,也易受虚假唤醒的攻击。

    注意,在这里变得“聪明”是非常不鼓励的。例如,决定“我将使用一个原子变量来避免在发信号时使用互斥量”实际上是行不通的。要么按照上面的方法教条行事,要么花几个月时间好好学习C++的线程和内存模型,以便即兴发挥。

        3
  •  -1
  •   João Paulo    6 年前

    例如,没有像windows api这样的状态。你的问题是新的线索 cv.notify_one(); 之前 wait() ,那你就白通知了。您可以模拟如下状态:

    std::mutex m;
    std::condition_variable cv;
    std::atomic_bool state; 
    
    void worker_thread()
    {
        std::this_thread::sleep_for(std::chrono::seconds(5)); //do some work here
        std::lock_guard<std::mutex> lk(m); //avoids cv to get notified before been in wait state
        state.store(true);      
        cv.notify_one();
    }
    
    int main()
    {
        state.store(false); //set initial state
        std::thread worker(worker_thread);
    
        std::cout << "Start waiting..." << std::endl;
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, [] {
            return state.load(); //returns ​false if the waiting should be continued. 
        });
        std::cout << "Finished waiting..." << std::endl;
    
        worker.join();
    
        std::cin.get();
        return 0;
    }