《C/C++ 面试 100 例》(十七)性能优化线程类析构死锁问题

本文通过一个实例展示了主线程删除正在运行的子线程导致的死锁问题。分析了WaitForSingleObject无限等待的原因,并提出了解决方案,即在确保子线程退出后再进行销毁,以防止主线程卡死。同时,增加了线程退出条件以控制子线程的正常停止。
摘要由CSDN通过智能技术生成

一、前言

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

二、引例

1、案例描述

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

2、案例实现

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

英雄哪里出来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值