牧者

大风起兮云飞扬

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

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的触发状态进行改变,从而实现线程间的通信和同步,这个主要是实现线程直接同步的一种方法。

posted on 2018-03-20 18:02  牧者.D  阅读(539)  评论(0)    收藏  举报