关于std :: condition_variables的两个问题

我一直在尝试弄清std::condition_variables,对 if (httpRequest.getMethod().equalsIgnoreCase("GET") && httpRequest.getRequesturi().toLowerCase().contains("expectedOperatinName")) 以及使用wait()还是notify_all感到特别困惑。

首先,我编写了一些代码并将其附加在下面。这是一个简短的解释:notify_one是一个包含一堆Collection对象的类。这些Counter对象具有Counter方法,需要在所有对象上一遍又一遍地调用该方法。为了加速所有工作,Counter::increment()还维护了一个线程池来分发工作,并使用其Collection方法发送所有工作。

这些线程不需要相互通信,并且Collection::increment_all()对象通常多于线程。只要一个线程比其他线程多处理Counter,就可以了,只要完成所有工作即可。将工作添加到队列很容易,只需要在“主”线程中完成即可。据我所知,唯一会发生的坏事是在工作进行期间是否允许在柜台上调用其他方法(例如Counter)。

Collection::printCounts

我在Ubuntu 18.04上使用#include <iostream> #include <thread> #include <vector> #include <mutex> #include <condition_variable> #include <queue> class Counter{ private: int m_count; public: Counter() : m_count(0) {} void increment() { m_count ++; } int getcount() const { return m_count; } }; class Collection{ public: Collection(unsigned num_threads,unsigned num_counters) : m_shutdown(false) { // start workers for(size_t i = 0; i < num_threads; ++i){ m_threads.push_back(std::thread(&Collection::work,this)); } // intsntiate counters for(size_t j = 0; j < num_counters; ++j){ m_counters.emplace_back(); } } ~Collection() { m_shutdown = true; for(auto& t : m_threads){ if(t.joinable()){ t.join(); } } } void printCounts() { // wait for work to be done std::unique_lock<std::mutex> lk(m_mtx); m_work_complete.wait(lk); // q2: do I need a while lop? // print all current counters for(const auto& cntr : m_counters){ std::cout << cntr.getcount() << ","; } std::cout << "\n"; } void increment_all() { std::unique_lock<std::mutex> lock(m_mtx); m_work_complete.wait(lock); for(size_t i = 0; i < m_counters.size(); ++i){ m_which_counters_have_work.push(i); } } private: void work() { while(!m_shutdown){ bool action = false; unsigned which_counter; { std::unique_lock<std::mutex> lock(m_mtx); if(m_which_counters_have_work.size()){ which_counter = m_which_counters_have_work.front(); m_which_counters_have_work.pop(); action = true; }else{ m_work_complete.notify_one(); // q1: notify_all } } if(action){ m_counters[which_counter].increment(); } } } std::vector<Counter> m_counters; std::vector<std::thread> m_threads; std::condition_variable m_work_complete; std::mutex m_mtx; std::queue<unsigned> m_which_counters_have_work; bool m_shutdown; }; int main() { int num_threads = std::thread::hardware_concurrency()-1; int num_counters = 10; Collection myCollection(num_threads,num_counters); myCollection.printCounts(); myCollection.increment_all(); myCollection.printCounts(); myCollection.increment_all(); myCollection.printCounts(); return 0; } 进行了编译,我认为代码可以实现所有这些目标,但是仍然存在一些问题:

  1. 我正在使用g++ -std=c++17 -pthread thread_pool.cpp -o tp && ./tp确保在开始打印所有新计数之前工作已完成。 为什么有时我会看到这段代码写在m_work_complete.wait(lk)循环中,或者用第二个参数作为lambda谓词函数? These docs提到虚假唤醒。如果发生虚假唤醒,是否意味着while可能会过早打印?如果是这样,我不要。我只想确保工作队列为空,然后再开始使用应该存在的数字。

  2. 我正在使用printCounts而不是m_work_complete.notify_all。我已经读过this thread,但我认为这并不重要-只有主线程会因此而被阻塞。 使用m_work_complete.notify_one更快,以便其他线程不必担心吗?

buxianwangluo 回答:关于std :: condition_variables的两个问题

  1. std::condition_variable并不是真正的条件变量,它更像是用于达到特定条件的同步工具。该条件由程序员决定,并且在每次condition_variable唤醒后仍应进行检查,因为当尚未达到所需条件时,它可能会虚假地唤醒,或“为时过早”

    在POSIX系统上,condition_variable::wait()委托给pthread_cond_wait,这容易引起虚假唤醒(请参阅“原理”部分中的“条件等待语义”)。在Linux上,pthread_cond_wait又通过futex实现,这又容易引起虚假唤醒。

    因此,是的,您仍然需要一个标志(由相同的互斥锁保护)或其他某种方式来检查工作是否真正完成。一种方便的方法是将检查包装在谓词中,然后将其传递给wait()函数,该函数将为您循环直到满足谓词为止。

  2. notify_all取消阻止所有等待条件变量的线程; notify_one仅解锁一个(或至少一个)。如果有多个等待线程,并且它们是等效的,即,任何一个线程都可以完全处理该条件,并且如果该条件足以让一个线程继续运行(例如将工作单元提交到线程池中),则{ {1}}会更有效率,因为它不会不必要地解除阻塞其他线程,使它们仅注意到没有要做的工作并回到等待状态。如果您只有一个服务员,那么notify_onenotify_one之间就不会有任何区别。

,

这很简单:何时使用notify()

  1. 没有理由需要一个以上的线程来了解该事件。 (例如,使用notify()来宣布工作线程将“消费”的项目的可用性,从而使该项目对其他工作人员不可用)。
    * AND *
  2. 没有任何错误线程可以唤醒。 (例如,如果所有线程都wait()位于同一确切函数的同一行中,则可能是安全的。)

在所有其他情况下,请使用notify_all()

本文链接:https://www.f2er.com/2352644.html

大家都在问