我一直在尝试弄清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;
}
进行了编译,我认为代码可以实现所有这些目标,但是仍然存在一些问题:
-
我正在使用
g++ -std=c++17 -pthread thread_pool.cpp -o tp && ./tp
确保在开始打印所有新计数之前工作已完成。 为什么有时我会看到这段代码写在m_work_complete.wait(lk)
循环中,或者用第二个参数作为lambda谓词函数? These docs提到虚假唤醒。如果发生虚假唤醒,是否意味着while
可能会过早打印?如果是这样,我不要。我只想确保工作队列为空,然后再开始使用应该存在的数字。 -
我正在使用
printCounts
而不是m_work_complete.notify_all
。我已经读过this thread,但我认为这并不重要-只有主线程会因此而被阻塞。 使用m_work_complete.notify_one
更快,以便其他线程不必担心吗?