VC++调用PostThreadMessage给线程发消息,实现线程间的通信(附源码)

Posted dvlinker

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了VC++调用PostThreadMessage给线程发消息,实现线程间的通信(附源码)相关的知识,希望对你有一定的参考价值。

       线程间通信的方式有多种,比如函数回调、(匿名)管道、发送消息等,对于Windows多线程,Windows提供了系统API函数PostThreadMessage可以给目标线程发消息,即通过发送消息的方式实现多线程间的通信。但使用PostThreadMessage函数时有些需要注意的细节,本文就来详细地介绍一下。

 

1、创建线程的函数返回时,新创建的线程的线程函数不一定运行起来了

       在Windows系统中,我们可以调用CreateThread__beginthreadex等函数去创建新的线程,在调用这些函数时指定线程对应的线程函数。当CreateThread或__beginthreadex返回时,只表示线程创建成功了,但线程函数不一定立即运行起来了

       有时我们需要在CreateThread或__beginthreadex返回后,等待线程函数运行起来,再进行后续操作。那我们如何知道线程函数已经运行起来呢?我们可以在调用CreateThread或__beginthreadex之前创建一个初始无信号的事件,然后把事件句柄作为参数传递给线程函数,然后在线程函数中将该事件设置为有信号。这样我们可以在CreateThread或__beginthreadex返回后,调用WaitForSIngleObject去等待这个事件。线程函数运行起来后,会将事件置为有信号,这样WaitForSIngleObject就返回了,这样主线程就知道新创建的线程的线程函数运行起来了,示例代码如下:

// 新创建的线程的线程函数
unsigned __stdcall ThreadFunc( LPVOID pParam )

	HANDLE hWaitEvent = pParam;
	// 将事件设置为有信号,告知主线程本线程函数已经运行起来
    SetEvent( hWaitEvent );

	// ...

    return 0;

// 创建一个初始无信号的事件
HANDLE hWaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
unsigned uThreadId = 0;
// 创建线程
HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0, ThreadFunc, (PVOID)hWaitEvent, 0, &uThreadId );

// 设置INFINITE参数,设置无限等待,直到事件变成有信号,即线程函数已经运行起来
WaitForSingleObject( hWaitEvent, INFINITE );
CloseHandle( hWaitEvent );

此外,经常有些新人搞不清楚新创建的线程何时退出,其实很简单,无外乎有两种情况:

1)外部直接调用TerminateThread强行将线程结束了,或者时线程内部自己调用ExitThread强行退出;

2)线程函数中的代码执行完了,线程函数退出了,这样整个线程就退出了。

2、在调用PostThreadMessage给新创建的线程发消息之前,要保证新线程的消息队列已经构建起来了

       我们在创建新线程后,可能需要给新线程发消息(消息中携带参数),通知新线程去执行指定的业务。如果在创建线程的函数CreateThread或__beginthreadex返回时立即调用PostThreadMessage给新线程发消息,新创建的线程可能接收不到消息,可能有两个原因:

1)新线程的线程函数还没运行起来;

2)新线程的线程函数运行起来了,但新线程的消息队列还没构建起来。

       对于第一个问题,上面我们已经说了解决办法。但对于第二个问题,如何知道新线程的消息队列已将建立起来了呢?我们可以调用PeekMessage强制系统立即构建新线程的消息队列,PeekMessage返回后就表示消息队列建立起来了,所以我们可以将上面例子中将事件置为有信号的代码移动到PeekMessage下面就可以,就能解决第二个问题了。相关的代码如下所示:

unsigned __stdcall ThreadFunc( LPVOID pParam )

	MSG msg;
	HANDLE hWaitEvent = pParam;

	// 强制系统立即给当前线程构建消息队列
	PeekMessage( &msg, NULL, WM_USER, WM_USER, PM_NOREMOVE );
	// 将事件设置为有信号,通知主线程当前线程的消息队列已经构建完成,可以发消息过来了
	SetEvent( hWaitEvent );

	// 调用GetMessage从当前线程的消息队列中拿出消息来处理
	while( ::GetMessage( &msg, NULL, 0, 0 ) )
	
		::TranslateMessage( &msg );
		::DispatchMessage( &msg );

		switch( msg.message )
		
		case MSG_XXXXXXX:  // 发送过来的自定义消息
			
				// do sometihing
				// ....
			
			break;
		case WM_QUIT:   // 退出
			return 0;
		default:
			break;

		
	

	return 0;
// 创建一个初始无信号的事件
HANDLE hWaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
unsigned uThreadId = 0;
HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0, ThreadFunc, (PVOID)hWaitEvent, 0, &uThreadId );

// 设置INFINITE参数,设置无限等待,直到事件变成有信号,即线程函数已经运行起来
WaitForSingleObject( hWaitEvent, INFINITE );
CloseHandle( hWaitEvent );

// 调用PostThreadMessage给新线程发送线程消息
PostThreadMessage( uThreadId, MSG_XXXXXXX, 0, 0 );

3、MSDN上对API函数PostThreadMessage的详细说明

       我们项目中也使用了PostThreadMessage,测试过程中发现有时调用PostThreadMessage给新创建的线程发送消息后,新线程并没有收到消息,导致相关业务流程出现异常。经过查看MSDN才得知我们上面分析出的原因,关于PostThreadMessage的这个问题,MSDN在该函数的说明页面中有详细的说明:

 

 

以上是关于VC++调用PostThreadMessage给线程发消息,实现线程间的通信(附源码)的主要内容,如果未能解决你的问题,请参考以下文章

函数PostThreadMessage

来自目标线程的 PostThreadMessage

PostThreadMessage

PostThreadMessage 到另一个进程

WPF 应用程序消息循环和 PostThreadMessage

给线上服务器增加一个新的域名