原文网址:http://my.oschina.net/laopiao/blog/94728

1.使用全局变量(窗体不适用) 
     实现线程间通信的方法有很多,常用的主要是通过全局变量、自定义消息和事件对象等来实现的。其中又以对全局变量的使用最为简洁。该方法将全局变量作为线程监视的对象,并通过在主线程对此变量值的改变而实现对子线程的控制。 
     由于这里的全局变量需要在使用它的线程之外对其值进行改变,这就需要通过volatile关键字对此变量进行说明。使用全局变量进行线程通信的方法非常简单,通过下面给出的示例代码能够对其有一个基本的认识。

// 线程通信用全局变量
                 volatile bool g_bDo = false;
                 ……
                 //线程处理函数
                 UINT ThreadProc5(LPVOID pParam)
                 {
                               //根据全局变量g_bDo的取值来决定线程的运行
                               while (g_bDo)
                               {           
                                            Sleep(2000);
                                            AfxMessageBox("线程正在运行!");
                               }

AfxMessageBox("线程终止");
                               return 0;
                 }
                 ……
                 void CSample06View::OnGlobalStart()
                 {
                               // 通过全局变量通知线程执行
                               g_bDo = true;

// 启动线程  
                               AfxBeginThread(ThreadProc5, NULL);
                 }
                 void CSample06View::OnGlobalEnd()
                 {
                               // 通过全局变量通知线程结束
                               g_bDo = false;
                 }

2.利用自定义消息(可适用于窗体)
     全局变量在线程通信中的应用多用在主线程对子线程的控制上,而从子线程向主线程的信息反馈则多采用自定义消息的方式来进行。这里对自定义消息的使用同使用普通自定义消息非常相似,只不过消息的发送是在子线程函数中进行的。该方法的主体是自定义消息,应首先定义自定义消息并添加对消息的响应代码。

// 自定义消息
                 #define WM_USER_MSG WM_USER + 101
                 ……
                 //消息响应函数在头文件中的定义:
                 //{{AFX_MSG(CSample06View)
                 //}}AFX_MSG
                 afx_msg void OnUserMsg(WPARAM wParam, LPARAM lParam);
                 DECLARE_MESSAGE_MAP()
                 ……
                 //消息映射
                 BEGIN_MESSAGE_MAP(CSample06View, CView)
                 //{{AFX_MSG_MAP(CSample06View)
                 //}}AFX_MSG_MAP
                 ON_MESSAGE(WM_USER_MSG, OnUserMsg)
                 END_MESSAGE_MAP()
                 ……
                 //消息响应函数
                 void CSample06View::OnUserMsg(WPARAM wParam, LPARAM lParam)
                 {
                               // 报告消息
                               AfxMessageBox("线程已退出!");
                 }

此后,在子线程函数需要向主线程发送消息的地方调用PostMessage()或SendMessage()消息传递函数将消息发送给主线程即可。由于消息发送函数是在线程中被调用,因此需要指出接受窗口句柄,可通过线程参数将其传递进线程函数。

UINT ThreadProc6(LPVOID pParam)
                 {
                               // 延迟一秒
                               Sleep(1000);

// 向主线程发送自定义消息
                               ::PostMessage((HWND)pParam, WM_USER_MSG, 0, 0);
                               return 0;
                 }
                 ……
                 void CSample06View::OnUseMessage()
                 {
                               // 获取窗口句柄
                               HWND hWnd = GetSafeHwnd();

// 启动线程
                               AfxBeginThread(ThreadProc6, hWnd);
                 }

3.使用事件内核对象(相当好用)
     利用事件(Event)内核对象对线程的通信要复杂些,主要通过对事件对象的监视来实现线程间的通信。事件对象由CreateEvent()函数来创建,具有两种存在状态:置位与复位,分别由SetEvent()和ResetEvent()来产生。事件的置位将通过 WaitForSingleObject()或WaitForMultipleObjects()之类的通知等待函数继续执行。

// 事件句柄
HANDLE hEvent = NULL;

UINT ThreadProc7(LPVOID pParam)
{
                 while(true)
                 {
                               // 等待事件发生
                               DWORD dwRet = WaitForSingleObject(hEvent, 0);
                               // 如果事件置位则退出线程,否则将继续执行
                               if (dwRet == WAIT_OBJECT_0)
                                            break;
                               else
                               {
                                            Sleep(2000);
                                            AfxMessageBox("线程正在运行!");
                               }
                 }
              
                 AfxMessageBox("线程终止运行!");
                 return 0;
}
……
void CSample06View::OnEventStart()
{
                 // 创建事件  
                 hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
              
                 // 启动线程
                 AfxBeginThread(ThreadProc7, NULL);
}

void CSample06View::OnEventEnd()
{
                 // 事件置位
                 SetEvent(hEvent);            
}

上面这段代码展示了事件对象在线程通信中的作用。在创建线程前首先创建一个事件对象hEvent,这里CreateEvent()函数所采用的四个参数分别表示句柄不能被继承、事件在置位后将由系统自动进行复位、事件对象初始状态为复位状态和不指定事件名。在创建的子线程中使用 WaitForSingleObject()对hEvent进行监视。WaitForSingleObject()的函数原型为:

DWORD WaitForSingleObject(
                               HANDLE hHandle,                             //等待对象的句柄
                               DWORD dwMilliseconds           //超过时间间隔
                 );

函数将在hHandle对象有信号时或是在等待时间超出由dwMilliseconds设定的超时时间间隔返回。其返回值可以为 WAIT_ABANDONED、WAIT_OBJECT_0和WAIT_TIMEOUT,分别表示被等待的互斥量(Mutex)对象没有被释放、等待的对象信号置位和超时。通过对返回值的判断可以区分出引起WaitForSingleObject()函数返回的原因。在本例中只关心 WAIT_OBJECT_0的返回值,当通过SetEvent()将hEvent置位后即可使WaitForSingleObject()立即返回并通过跳出循环而结束线程。

 注意:自己添加的消息处理函数的返回值必须是LRESULT ,否则会出现“ON_MESSAGE转换类型无效”的提示信息!!!

【转】VC 线程间通信的三种方式的更多相关文章

  1. VC 线程间通信的三种方式

    1.使用全局变量(窗体不适用)     实现线程间通信的方法有很多,常用的主要是通过全局变量.自定义消息和事件对象等来实现的.其中又以对全局变量的使用最为简洁.该方法将全局变量作为线程监视的对象,并通 ...

  2. 线程间通信的三种方式(NSThread,GCD,NSOperation)

    一.NSThread线程间通信 #import "ViewController.h" @interface ViewController ()<UIScrollViewDel ...

  3. 容器间通信的三种方式 - 每天5分钟玩转 Docker 容器技术(35)

    容器之间可通过 IP,Docker DNS Server 或 joined 容器三种方式通信. IP 通信 从上一节的例子可以得出这样一个结论:两个容器要能通信,必须要有属于同一个网络的网卡. 满足这 ...

  4. Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  5. Java线程间通信-回调的实现方式

    Java线程间通信-回调的实现方式   Java线程间通信是非常复杂的问题的.线程间通信问题本质上是如何将与线程相关的变量或者对象传递给别的线程,从而实现交互.   比如举一个简单例子,有一个多线程的 ...

  6. 多线程之线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  7. 19、Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  8. [转] 微信小程序页面间通信的5种方式

    微信小程序页面间通的5种方式 PageModel(页面模型)对小程序而言是很重要的一个概念,从app.json中也可以看到,小程序就是由一个个页面组成的. 如上图,这是一个常见结构的小程序:首页是一个 ...

  9. Angular 组件通信的三种方式

    我们可以通过以下三种方式来实现: 传递一个组件的引用给另一个组件 通过子组件发送EventEmitter和父组件通信 通过serive通信 1. 传递一个组件的引用给另一个组件 Demo1 模板引用变 ...

随机推荐

  1. Oracle like '%...%' 优化

    Oracle like '%...%' 优化 1.尽量不要使用 like '%%' 2.对于 like '%' (不以 % 开头),Oracle可以应用 colunm上的index 3.对于 like ...

  2. less开发指南(一)- 小牛试刀

    [一]less简介 LESS(是.less后缀名的文件) 包含一套自定义的语法及一个解析器,我们根据这些语法定义自己的样式规则,这些规则最终会通过解析器,编译生成对应的 CSS 文件.LESS 并没有 ...

  3. 四元数 Quaternion

    最近在重写自己游戏引擎的场景管理模块,重温了一下有关四元数的一些知识,在此做一下简单的笔记. 四元数可以用来准确地描述三维矢量的旋转,并且可以有效地表达多个旋转操作的叠加,因此在三维游戏引擎的场景管理 ...

  4. 团队项目:"Jarvis For Chat"

    "Jarvis For Chat"项目简介 项目详情信息已经在上一篇博客中详细给出,详请查看博客 团队成员 姓名 学号 张扬(队长) 031602345 苏韫月 031602631 ...

  5. CentOS7 修改默认时区为 北京时间

    首先同步时间 yum install -y ntpdate ntpdate -u cn.pool.ntp.org 然后设置中国时区(北京时间) timedatectl set-timezone Asi ...

  6. win10 downloader.exe 修复方法

    由于系统更新是和系统密切相关的,在日常使用中某些系统文件如果出现损坏,同样会导致系统更新无法成功安装.如果在使用Windows 10时可以成功下载更新,但是安装的时候出错,此时就需要对系统文件进行检查 ...

  7. The main points of capacitive screen technology

  8. jQuery 滑动选项卡jQuery tabslet

    Tabslet   Yet another jQuery plugin for tabs, lightweight, easy to use and with some extra features ...

  9. Java之引用类型分析(SoftReference/WeakReference/PhantomReference)

    引言: 即使对于Java的很多老鸟来说,如果忽然问他引用的类型,大概率是一脸茫然,不知所措的-.Java中的引用还分类型,神马情况??? 本文将针对这些类型进行分析,帮助您一文知所有类型. Java的 ...

  10. D. Closest Equals(线段树)

    题目链接: D. Closest Equals time limit per test 3 seconds memory limit per test 256 megabytes input stan ...