终止自定义 std::thread 池
Terminating custom std::thread pool
我最近一直在研究多线程游戏引擎架构和线程池。现在我实现了一个基本的 Kernel
class。这个class有一个std::vector<std::thread>
,代表线程池。现在,池中的单个线程 运行 执行以下函数:
while(m_IsRunning)
{
std::unique_lock<std::mutex> kernelstateLocker(m_KernelStateMutex);
m_KernelStateCond.wait(kernelstateLocker);
if(m_KernelState == KernelState::KernelShutdown || m_KernelState == KernelState::KernelTerminate)
{
kernelstateLocker.unlock();
//std::cout << "Worker #" << _workerID << std::endl console log here
break;
}
else if(m_KernelState == KernelState::KernelWorkAvailable)
{
...
}
如您所见,如果 KernelState
变量发生变化,线程就会被唤醒。当任务添加到队列中或内核关闭时,可能会发生这种情况。内核关闭条件变量由主程序线程通过 m_KernelStateCond.notify_all()
调用。但是,正如我在评论中看到的那样添加了 cout
,有时最多 8 个工作线程中只有一个会打印其名称和 ID,这表明其他线程从未终止。有谁知道这是为什么,以及我如何终止池中的所有线程?以防万一,我的平台是 Windows 10 64 位上的 TDM-GCC-64 5.1。
更新:
根据评论请求和SO规则,这是调用条件变量的代码。
std::unique_lock<std::mutex> shutdownLocker(m_IsRunningMutex);
m_ShutdownCond.wait(shutdownLocker, [this](){ return !m_IsRunning; });
if(!m_IsRunning)
{
shutdownLocker.unlock();
m_KernelStateMutex.lock();
m_KernelState = KernelState::KernelShutdown;
m_KernelStateMutex.unlock();
m_KernelStateCond.notify_all();
}
我很确定我的这部分代码可以正常工作,因为至少有一个线程工作者实际关闭了。为了完整起见,这是我的完整 Kernel
class:
class Kernel : public Singleton<Kernel>
{
public:
void boot(unsigned int _workerCount);
void run();
void shutdown();
void addTask(std::shared_ptr<Task> _task);
private:
friend class Singleton<Kernel>;
Kernel();
~Kernel();
bool m_IsRunning;
KernelState m_KernelState;
std::vector<std::thread> m_Workers;
std::queue<std::shared_ptr<Task>> m_Tasks;
std::vector<std::shared_ptr<Task>> m_LoopTasks;
std::condition_variable m_KernelStateCond;
std::mutex m_KernelStateMutex;
void workTask(unsigned int _workerID);
};
我解决了这个问题。这不是我的线程池实现本身的问题,但它与内核关闭时仍有任务到达这一事实有关。所以一些工作线程永远不会关闭,因此,内核卡住了。向任务添加约束解决了这个问题。
我最近一直在研究多线程游戏引擎架构和线程池。现在我实现了一个基本的 Kernel
class。这个class有一个std::vector<std::thread>
,代表线程池。现在,池中的单个线程 运行 执行以下函数:
while(m_IsRunning)
{
std::unique_lock<std::mutex> kernelstateLocker(m_KernelStateMutex);
m_KernelStateCond.wait(kernelstateLocker);
if(m_KernelState == KernelState::KernelShutdown || m_KernelState == KernelState::KernelTerminate)
{
kernelstateLocker.unlock();
//std::cout << "Worker #" << _workerID << std::endl console log here
break;
}
else if(m_KernelState == KernelState::KernelWorkAvailable)
{
...
}
如您所见,如果 KernelState
变量发生变化,线程就会被唤醒。当任务添加到队列中或内核关闭时,可能会发生这种情况。内核关闭条件变量由主程序线程通过 m_KernelStateCond.notify_all()
调用。但是,正如我在评论中看到的那样添加了 cout
,有时最多 8 个工作线程中只有一个会打印其名称和 ID,这表明其他线程从未终止。有谁知道这是为什么,以及我如何终止池中的所有线程?以防万一,我的平台是 Windows 10 64 位上的 TDM-GCC-64 5.1。
更新:
根据评论请求和SO规则,这是调用条件变量的代码。
std::unique_lock<std::mutex> shutdownLocker(m_IsRunningMutex);
m_ShutdownCond.wait(shutdownLocker, [this](){ return !m_IsRunning; });
if(!m_IsRunning)
{
shutdownLocker.unlock();
m_KernelStateMutex.lock();
m_KernelState = KernelState::KernelShutdown;
m_KernelStateMutex.unlock();
m_KernelStateCond.notify_all();
}
我很确定我的这部分代码可以正常工作,因为至少有一个线程工作者实际关闭了。为了完整起见,这是我的完整 Kernel
class:
class Kernel : public Singleton<Kernel>
{
public:
void boot(unsigned int _workerCount);
void run();
void shutdown();
void addTask(std::shared_ptr<Task> _task);
private:
friend class Singleton<Kernel>;
Kernel();
~Kernel();
bool m_IsRunning;
KernelState m_KernelState;
std::vector<std::thread> m_Workers;
std::queue<std::shared_ptr<Task>> m_Tasks;
std::vector<std::shared_ptr<Task>> m_LoopTasks;
std::condition_variable m_KernelStateCond;
std::mutex m_KernelStateMutex;
void workTask(unsigned int _workerID);
};
我解决了这个问题。这不是我的线程池实现本身的问题,但它与内核关闭时仍有任务到达这一事实有关。所以一些工作线程永远不会关闭,因此,内核卡住了。向任务添加约束解决了这个问题。