C++ 线程类析构死锁问题

    科技2022-08-06  104

    文章目录

    一、前言二、引例1、案例描述2、案例实现 三、问题分析一、输出二、分析 四、改进方案

    一、前言

    本文将介绍一种主线程主动 delete 子线程对象,导致主线程卡死(无限等待)的情况;

    二、引例

    1、案例描述

    1)实现包括一个主线程和一个子线程;2)主线程负责启动子线程,并且在子线程运行 500ms 后进行销毁:3)子线程每 16ms 输出一条保活信息;

    2、案例实现

    子线程 LogicThread 继承 Thread(Thread 类实现参考); #include "Thread.h" class LogicThread : public Thread { public: LogicThread() : m_bStop(false) { } protected: virtual void DoWork() { while (!m_bStop) { printf("Thread[%d]: I'm active!\n", GetId()); Sleep(16); } printf("Thread[%d]: I'm exit!\n", GetId()); } private: bool m_bStop; }; int main() { LogicThread *pLS = new LogicThread; pLS->Start(); Sleep(500); delete pLS; return 0; } 这段代码的 delete 能够顺利执行吗?答案是不能!

    三、问题分析

    一、输出

    首先运行起来看下控制台输出,线程会无限循环的输出 I’m active!: Thread[70508]: I'm active! Thread[70508]: I'm active! Thread[70508]: I'm active! Thread[70508]: I'm active! Thread[70508]: I'm active! Thread[70508]: I'm active! ...

    二、分析

    分析的突破口就在 delete 的调用;如果这里没有 delete ,那么主线程可以顺利走到 return 0;但是加上 delete 以后,会调用到基类 Thread 的析构,从而调用子线程的 Stop() 接口,实现如下: Thread::~Thread() { Stop(); } void Thread::Stop() { if (!m_pkHandle) { return; } WaitForSingleObject((HANDLE)m_pkHandle, INFINITE); CloseHandle((HANDLE)m_pkHandle); m_pkHandle = NULL; } 由于子线程一直在运行没有返回,所以 WaitForSingleObject 会一直 阻塞等待,这个调用是在主线程进行的,所以现象就是主线程卡死了;

    四、改进方案

    为了让 WaitForSingleObject 不会无限等待,需要确保线程函数退出以后,才能进行逻辑线程的销毁;所以可以在主线程轮询子线程状态,当子线程进入退出状态方能执行退出逻辑;为了把问题描述清楚,这里增加一个线程结束条件,计数器 100 计数完毕 则将 m_bStop 置 true 让线程停下来; #include "Thread.h" class LogicThread : public Thread { public: LogicThread() : m_bStop(false) { } protected: virtual void DoWork() { int nCnt = 100; while (!m_bStop) { printf("Thread[%d]: I'm active!\n", GetId()); Sleep(16); if (--nCnt == 0) { m_bStop = true; // 增加结束条件 } } printf("Thread[%d]: I'm exit!\n", GetId()); } private: bool m_bStop; }; int main() { LogicThread *pLS = new LogicThread; pLS->Start(); // 主线程等待子线程退出; do { Sleep(16); } while (pLS->IsRunning()); // 清理线程信息 delete pLS; return 0; }
    Processed: 0.012, SQL: 8