【问题标题】:Terminating custom std::thread pool终止自定义 std::thread 池
【发布时间】:2016-03-10 13:31:22
【问题描述】:

我最近一直在玩多线程游戏引擎架构和线程池。现在我实现了一个基本的Kernel 类。这个类有一个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 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);
};

【问题讨论】:

  • 请发帖minimal reproducible example。如果我猜的话,那是因为您在通知条件变量之前没有获取互斥锁。为了保证所有相关线程的正确排序,还必须获取用于等待条件变量的同一个互斥锁,以便通知同一个条件变量。但是,由于您没有显示相关代码,因此无法给出权威答案,直到您编辑帖子并包含minimal reproducible example,并强调“最小”和“完整”部分。
  • P.S.您不需要显式的“kernelstateLocker.unlock();”当唯一锁超出范围并被销毁时,它会自动处理。
  • 使用许多现有线程池实现之一可能更容易。使用 boost::asio 更容易 (example)
  • @SamVarshavchik 没错,你说得对,唯一的锁。忘记了。更新了我的答案,这样更好吗?
  • @rustyx:我不是 boost 的粉丝,出于我自己的原因,我喜欢避免它,因为 STL 和自定义实现可以很好地完成这项工作。

标签: c++ multithreading c++11 threadpool


【解决方案1】:

我发现了问题所在。我的线程池实现本身没有问题,但这与内核关闭时仍有任务到达的事实有关。所以一些工作线程永远不会关闭,因此内核被卡住了。向任务添加约束解决了这个问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-19
    • 1970-01-01
    • 2018-05-10
    • 1970-01-01
    • 1970-01-01
    • 2021-12-10
    • 1970-01-01
    • 2021-04-02
    相关资源
    最近更新 更多