C++ std::condition_variable::notify_all()只唤醒线程池中的一个线程
我想写一个简单的线程池来学习它们是如何在引擎盖下工作的。不过,我遇到了一个问题。当我使用条件变量并调用notify_all()时,它只唤醒池中的一个线程 其他一切都很好。我已经排了900个工作,每个工作都有一个不错的负载。一个醒来的线程消耗了所有这些作业,然后又回到睡眠状态。在下一个循环中,这一切再次发生 问题是,只有一个线程可以工作!我是怎么搞砸这个模板的 ThreadPool.h:C++ std::condition_variable::notify_all()只唤醒线程池中的一个线程,c++,threadpool,std,condition-variable,C++,Threadpool,Std,Condition Variable,我想写一个简单的线程池来学习它们是如何在引擎盖下工作的。不过,我遇到了一个问题。当我使用条件变量并调用notify_all()时,它只唤醒池中的一个线程 其他一切都很好。我已经排了900个工作,每个工作都有一个不错的负载。一个醒来的线程消耗了所有这些作业,然后又回到睡眠状态。在下一个循环中,这一切再次发生 问题是,只有一个线程可以工作!我是怎么搞砸这个模板的 ThreadPool.h: #pragma once #include <mutex> #include <stack
#pragma once
#include <mutex>
#include <stack>
#include <atomic>
#include <thread>
#include <condition_variable>
class ThreadPool
{
friend void __stdcall ThreadFunc();
public:
static ThreadPool & GetInstance()
{
static ThreadPool sInstance;
return (sInstance);
}
public:
void AddJob(Job * job);
void DoAllJobs();
private:
Job * GetJob();
private:
const static uint32_t ThreadCount = 8;
std::mutex JobMutex;
std::stack<Job *> Jobs;
volatile std::atomic<int> JobWorkCounter;
std::mutex SharedLock;
std::thread Threads[ThreadCount];
std::condition_variable Signal;
private:
ThreadPool();
~ThreadPool();
public:
ThreadPool(ThreadPool const &) = delete;
void operator = (ThreadPool const &) = delete;
};
#pragma一次
#包括
#包括
#包括
#包括
#包括
类线程池
{
friend void_uustdcall ThreadFunc();
公众:
静态线程池&GetInstance()
{
静态线程池;
返回(sInstance);
}
公众:
void AddJob(Job*Job);
void DoAllJobs();
私人:
Job*GetJob();
私人:
const static uint32_t ThreadCount=8;
std::mutex job mutex;
std::堆栈作业;
挥发性标准::原子作业计数器;
std::互斥共享锁;
标准::线程数[线程数];
std::条件变量信号;
私人:
线程池();
~ThreadPool();
公众:
线程池(线程池常量&)=删除;
void运算符=(线程池常量&)=删除;
};
ThreadPool.cpp:
#include "ThreadPool.h"
void __stdcall ThreadFunc()
{
std::unique_lock<std::mutex> lock(ThreadPool::GetInstance().SharedLock);
while (true)
{
ThreadPool::GetInstance().Signal.wait(lock);
while (Job * job = ThreadPool::GetInstance().GetJob())
{
job->_jobFn(job->_args);
ThreadPool::GetInstance().JobWorkCounter--;
}
}
}
ThreadPool::ThreadPool()
{
JobWorkCounter = 0;
for (uint32_t i = 0; i < ThreadCount; ++i)
Threads[i] = std::thread(ThreadFunc);
}
ThreadPool::~ThreadPool()
{
}
void ThreadPool::AddJob(Job * job)
{
JobWorkCounter++;
JobMutex.lock();
{
Jobs.push(job);
}
JobMutex.unlock();
}
void ThreadPool::DoAllJobs()
{
Signal.notify_all();
while (JobWorkCounter > 0)
{
Sleep(0);
}
}
Job * ThreadPool::GetJob()
{
Job * return_value = nullptr;
JobMutex.lock();
{
if (Jobs.empty() == false)
{
return_value = Jobs.top();
Jobs.pop();
}
}
JobMutex.unlock();
return (return_value);
}
#包括“ThreadPool.h”
void uu stdcall ThreadFunc()
{
std::unique_lock锁(ThreadPool::GetInstance().SharedLock);
while(true)
{
线程池::GetInstance().Signal.wait(锁);
while(Job*Job=ThreadPool::GetInstance().GetJob())
{
作业->作业fn(作业->参数);
ThreadPool::GetInstance().JobWorkCounter--;
}
}
}
线程池::线程池()
{
作业计数器=0;
对于(uint32_t i=0;i0)
{
睡眠(0);
}
}
作业*线程池::GetJob()
{
作业*返回值=nullptr;
JobMutex.lock();
{
if(Jobs.empty()==false)
{
return_value=Jobs.top();
Jobs.pop();
}
}
JobMutex.unlock();
返回值(返回值);
}
谢谢你的帮助!抱歉发了这么大的代码帖子
std::unique_lock<std::mutex> lock(ThreadPool::GetInstance().SharedLock);
当主线程执行notify_All()
时,所有线程都将收到来自条件变量的信号,但您忘记了一个关键细节:在收到条件变量通知后醒来后,互斥锁将自动重新锁定。这就是wait()
的工作原理。在C++书籍或手册页中阅读它的文档;只有一个线程能够做到这一点。所有其他醒来的线程也将尝试锁定互斥锁,但只有第一个线程赢得比赛并将这样做,而所有其他线程将睡眠并继续做梦
收到通知后的线程将不从wait()
返回,直到该线程也成功地重新锁定互斥锁
要从wait()
返回,必须发生两件事:线程从条件变量得到通知,并且线程成功地重新锁定互斥锁wait()
解锁互斥锁并原子地等待条件变量,并在收到通知时重新锁定互斥锁
因此,幸运线程将锁定互斥锁,并继续清空所有作业的队列,然后返回循环顶部并再次wait()
。这将解锁互斥锁,现在其他一些幸运的线程,已经被通知,但耐心等待它的机会沐浴在阳光和荣耀中,将能够锁定互斥锁。以这种方式,所有其他线程将轮流,象式,醒来,检查作业队列,在那里找不到任何东西,然后进入睡眠
这就是你看到这种行为的原因
要使显示的代码线程安全,必须做两件基本的事情
1) 您不需要两个互斥体,一个就足够了
2) 在对条件变量执行wait()
ing操作之前,请检查作业队列中是否有内容。如果有问题,请将其删除,并解锁互斥锁,然后执行此操作
3) wait()
仅当作业队列为空时。在wait()
返回后,重新锁定互斥锁,然后检查作业队列是否仍然为空(此时不能真正保证它不是空的,只能保证它可能是非空的)
您只需要一个互斥来保护对非线程安全作业队列的访问,并等待条件变量
当主线程执行notify_All()
时,所有线程都将收到来自条件变量的信号,但您忘记了一个关键细节:在收到条件变量通知后醒来后,互斥锁将自动重新锁定。这就是wait()
的工作原理。在C++书籍或手册页中阅读它的文档;只有一个线程能够做到这一点。所有其他醒来的线程也将尝试锁定互斥锁,但只有第一个线程赢得比赛并将这样做,而所有其他线程将睡眠并继续做梦
收到通知后的线程将不从wait()
返回,直到该线程也成功地重新锁定互斥锁
要从wait()
返回,必须发生两件事:线程从条件变量得到通知,并且线程成功地重新锁定互斥锁<代码>等待()解锁mu
ThreadPool::GetInstance().Signal.wait(lock);
std::condition_variable cv;
mutable std::mutex m;
your_message_type message;
std::unique_lock l{m}; // C++17, don't need to pass type
set_message_data(message);
cv.notify_one();
std::unique_lock l{m};
set_lots_of_message_data(message);
cv.notify_all();
while(true) {
auto data = [&]()->std::optional<data_to_process>{
std::unique_lock l{m};
cv.wait( l, [&]{ return done() || there_is_a_message(message); } );
if (done()) return {};
return get_data_to_process(message);
}();
if (!data) break;
auto& data_to_process = *data;
// process the data
}