C++最基本的线程管理(完整源码)

初级代码游戏的专栏介绍与文章目录-CSDN博客

        讲起编程理论天花乱坠,现实却跟山顶洞人一样。

目录

一、问题

二、跟踪线程创建

三、多线程的一般原则

四、相关技术点

4.1 CreateThread

4.2 CloseHandle

4.3 GetExitCodeThread

4.4 TerminateThread


一、问题

        前两年维护一些老程序,发现这么用线程的:

HANDLE hThread=CreateThread(。。。。。。);
CloseHandle(hThread);		

        就是这么潇洒!

        是不是线程里面有高级机制控制啊?我啃了源代码啊,没有,真的没有。

        为啥啃源代码啊,随机BUG了呗。虽然不确定BUG怎么引起的,先解决不受控的线程总是没错的。

二、跟踪线程创建

        所以要记录有哪些线程、检测线程状态,为此写了一个这么一个类:

//线程管理,目前主要用来在结束时先关闭子线程
class CThreadManager
{
private:
	struct struct_thread_info
	{
		HANDLE hThread;
		string file;//创建此线程的代码位置
		int line;//创建此线程的代码位置
	};
	vector<struct_thread_info > m_managered_threads;

	void ClearFinishedThread(bool stop);
public:
	void AddThreadHandle(HANDLE h, char const* file, int line)
	{
		ClearFinishedThread(false);

		struct_thread_info tmp;
		tmp.hThread = h;
		tmp.file = file;
		tmp.line = line;

		m_managered_threads.push_back(tmp);
	}
	void StopAllThread();
};
extern CThreadManager g_CThreadManager;
#define AddManageredThread(h) g_CThreadManager.AddThreadHandle((h),__FILE__,__LINE__)

        结构struct_thread_info记录线程句柄和创建位置,方便找到相关代码。

        宏AddManageredThread(h)简化了调用方法。

        全局对象和两个成员函数:

CThreadManager g_CThreadManager;

void CThreadManager::ClearFinishedThread(bool stop)
{
	for (int i = m_managered_threads.size() - 1; i >= 0; --i)
	{
		DWORD exitcode;
		if (!GetExitCodeThread(m_managered_threads[i].hThread, &exitcode))
		{
			MessageBox(NULL, "GetExitCodeThread失败", "出错", 0);
			return;
		}
		if (STILL_ACTIVE != exitcode)
		{
			CloseHandle(m_managered_threads[i].hThread);
			m_managered_threads.erase(m_managered_threads.begin() + i);
		}
		else
		{
			if (stop)
			{
				TerminateThread(m_managered_threads[i].hThread, 1);
				CloseHandle(m_managered_threads[i].hThread);
				m_managered_threads.erase(m_managered_threads.begin() + i);
			}
		}
	}
}

void CThreadManager::StopAllThread()
{
	ClearFinishedThread(true);
}

        调用的时候用AddManageredThread代替CloseHandle就可以了,程序退出的时候先执行StopAllThread结束所有子线程,免得因为退出的先后顺序出异常。

        AddManageredThread的特点是自动回收已经结束的子线程(所以在程序结束前最多存在一个已经结束而未回收的线程)。GetExitCodeThread获取子线程结束状态。

        上面的代码在结束时直接强制终止了子线程,安全的方法是设置全局变量,所有子线程不时检测全局变量状态,实现受控的退出。对于控制线程退出而言,全局变量已经足够好了,其它机制,比如信号、消息,只会带来不必要的复杂度而不会减轻任何编码量。

        CloseHandle的特点是让子线程结束后自动回收,但是不知道子线程什么时候结束嘛,程序结束前难免手动清理一些资源,而子线程却在使用这些资源,这不就BUG了嘛。

三、多线程的一般原则

        多线程程序最基本的原则就是子线程应该是受管理的,最起码的,就像上面写的类一样,要把创建和停止管理起来,在程序退出的时候先关闭所有子线程,再退出主程序。

        线程的交互尽量简单,尽量用一个独立的交互区来操作,不要让每个线程都可以随意使用整个程序的数据,数据保护很难做。

        线程里面不要再创建线程,很难管理。

        从基本完成一个程序到解决随机BUG很漫长。学会使用CreateThread不算什么,功夫都在自己的代码逻辑里。

四、相关技术点

4.1 CreateThread

HANDLE CreateThread(
  [in, optional]  LPSECURITY_ATTRIBUTES   lpThreadAttributes,
  [in]            SIZE_T                  dwStackSize,
  [in]            LPTHREAD_START_ROUTINE  lpStartAddress,
  [in, optional]  __drv_aliasesMem LPVOID lpParameter,
  [in]            DWORD                   dwCreationFlags,
  [out, optional] LPDWORD                 lpThreadId
);

        创建线程,参数很多不过大部分都可以不用。

4.2 CloseHandle

BOOL CloseHandle(
  [in] HANDLE hObject
);

        关闭句柄。这个API的意思并不是删除句柄代表的资源,只是表示结束对资源的使用。对于线程而言,这个操作并不表示结束线程,而是表示让它运行完了自己结束就好了。

        线程的访问计数和别的资源不一样,线程一启动计数就是2,CloseHandle只会减少1,所以CloseHandle之后线程还有计数1,仍是有效的。 

4.3 GetExitCodeThread

BOOL GetExitCodeThread(
  [in]  HANDLE  hThread,
  [out] LPDWORD lpExitCode
);

        获取线程退出状态。如果线程没有退出,要自己想办法,直接杀死线程太粗暴了。

4.4 TerminateThread

BOOL TerminateThread(
  [in, out] HANDLE hThread,
  [in]      DWORD  dwExitCode
);

         杀死线程,很不好。dwExitCode设置线程返回值——你自己杀死的你不知道咋回事吗?这个返回值是给调用GetExitCodeThread的人看的,不是给自己看的。

        以上代码是以win7+vs2010为目标的,可在win10、win11上运行。

(这里是文档结束)

  • 19
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值