c++ 多线程 03 通信
1.全局变量
由于同一进程下的线程之间共享数据空间。当需要有多个线程来访问一个全局变量时,通常我们会在这个全局变量前加上volatile声明,以防编译器对此变量进行优化。
2.Message消息机制
常用的Message通信的接口主要有两个:PostMessage和PostThreadMessage,
PostMessage为线程向主窗口发送消息。而PostThreadMessage是任意两个线程之间的通信接口。
PostMessage()
函数原型:
B00L PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
参数:
hWnd:其窗口程序接收消息的窗口的句柄。可取有特定含义的两个值:
HWND.BROADCAST:消息被寄送到系统的所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口
和弹出式窗口。消息不被寄送到子窗口。
NULL:此函数的操作和调用参数dwThread设置为当前线程的标识符PostThreadMessage函数一样。
Msg:指定被寄送的消息。
wParam:指定附加的消息特定的信息。
IParam:指定附加的消息特定的信息。
返回值:如果函数调用成功,返回非零值:如果函数调用失败,返回值是零。
MS还提供了SendMessage方法进行消息间通讯,SendMessage(),他和PostMessage的区别是:
SendMessage是同步的,而PostMessage是异步的。SendMessage必须等发送的消息执行之后,才返回。
PostThreadMessage方法可以将消息发送到指定线程。
函数原型:BOOL PostThreadMessage(DWORD idThread,UINT Msg,WPARAM wParam, LPARAM lParam);
参数除了ThreadId之外,基本和PostMessage相同。
目标线程通过GetMessage()方法来接受消息。
注:使用这个方法时,目标线程必须已经有自己的消息队列。否则会返回ERROR_INVALID_THREAD_ID错误。可以用
PeekMessage()给线程创建消息队列。
PostThreadMessage可以用于线程之间的异步通讯,因为它不用等待调用者返回,这也许是线程通讯中最简单的一种方法了。但是要注意以下问题。
1 .PostThreadMessage有时会失败,报1444错误(Invalid thread identifier. )其实这不一定是线程不存在的原因,也有可能是线程不存在消息队列(message queue)造成的。事实上,并不是每个thread都有message queue,那如何让thread具有呢?答案是,至少调用message相关的function一次,比如GetMessage,PeekMessage。
2.如果是post动态分配的memory给另外一个thread,要注意内存的正确释放。
3.PostThreadMessage不能够post WM_COPYDATE之类的同步消息,否则会报错
4.最好不要使用PostThreadMessage post message给一个窗口,使用PostMessage替代。
下面是我写的一个比较严整的例子,仅供参考。
// 123.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <stdlib.h> #include <stdio.h> #include <windows.h> #define MY_MSG (WM_USER + 1) DWORD WINAPI Thread1(LPVOID para) { DWORD dwThreadId = *(DWORD *)para; DWORD i = 0; TCHAR *p; char strTmp[100]; while(TRUE) { Sleep(1700); p = new TCHAR[10]; sprintf_s(strTmp, 100, "Hello %d %x ", i++, p); PostThreadMessage(dwThreadId, MY_MSG, (WPARAM)strTmp, (LPARAM)p); //delete []p; } return 0; } DWORD WINAPI Thread2(LPVOID para) { MSG msg; PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); while(true) { if(GetMessage(&msg,0,0,0)) //get msg from message queue { switch(msg.message) { case MY_MSG: char * pInfo = (char *)msg.wParam; printf("recv:%s\n",pInfo); delete [](TCHAR *)msg.lParam; break; } } }; return 0; } int main() { DWORD dwValue = GetCurrentThreadId(); HANDLE hThread1 = CreateThread(NULL, 0, &Thread2, &dwValue, 0, &dwValue); // &Thread1可写作Thread1, 都是函数指针. HANDLE hThread2 = CreateThread(NULL, 0, &Thread1, &dwValue, 0, NULL); CloseHandle(hThread1); CloseHandle(hThread2); MSG msg; while(GetMessage(&msg, NULL, 0, 0)) { switch(msg.message) { case MY_MSG: printf("msg: 0x%x w: %x ws: %s l: %x\n", msg.message, &msg.wParam, msg.wParam, msg.lParam); delete [](TCHAR *)msg.lParam; //注释掉这句你就会看到堆内存地址变化 break; default: printf("Unknown msg:0x%x\n",msg.message); break; } Sleep(1); } return 0; }
3.CEvent对象
CEvent为MFC中的一个对象,可以通过对CEvent的触发状态进行改变,从而实现线程间的通信和同步,这个主要是实现线程直接同步的一种方法。

浙公网安备 33010602011771号