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()立即返回并通过跳出循环而结束线程。

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

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

    原文网址:http://my.oschina.net/laopiao/blog/94728 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. mac 下 outlook 邮箱 服务器端口设置

  2. Selenium2+python自动化35-获取元素属性

    前言 通常在做断言之前,都要先获取界面上元素的属性,然后与期望结果对比.本篇介绍几种常见的获取元素属性方法. 一.获取页面title 1.有很多小伙伴都不知道title长在哪里,看下图左上角. 2.获 ...

  3. 利用 FastCoding 将对象进行本地持久化

    FastCoding https://github.com/nicklockwood/FastCoding A faster and more flexible binary file format ...

  4. bashrc和profile的用途和区别

    使用终端登录Linux操作系统的控制台后,会出现一个提示符号(例如:#或~),在这个提示符号之后可以输入命令,Linux根据输入的命令会做回应,这一连串的动作是由一个所谓的Shell来做处理. She ...

  5. kafka存储机制

    kafka存储机制 @(博客文章)[storm|大数据] kafka存储机制 一关键术语 二topic中partition存储分布 三 partiton中文件存储方式 四 partiton中segme ...

  6. iOS开发-搜索栏UISearchBar和UISearchController

    iOS中UISearchDisplayController用于搜索,搜索栏的重要性我们就不说了,狼厂就是靠搜索起家的,现在越来越像一匹没有节操的狼,UC浏览器搜索栏现在默认自家的神马搜索,现在不管是社 ...

  7. MongoDB Sort op eration used more than the maximum 33554432 bytes of RAM. Add an index, or speci fy a smaller limit.

    最近在获取mongodb某个集合的数据过程中,在进行排序的过程中报错,具体报错信息如下: Error: error: { , "errmsg" : "Executor e ...

  8. 【SpringBoot】SpringBoot性能优化

    Spring 框架给企业软件开发者提供了常见问题的通用解决方案,包括那些在未来开发中没有意识到的问题.但是,它构建的 J2EE 项目变得越来越臃肿,逐渐被 Spring Boot 所替代.Spring ...

  9. 【cocos2dx开发技巧8】自定义控件-使自定义控件具有RGBA特性

    转发,请保持地址:http://blog.csdn.net/stalendp/article/details/9948545 虽然CCNodeRGBA,CCLayerRGBA,sprite等提供颜色和 ...

  10. windows server2012部署Cognos问题小结

    一:数据库配置问题   1:.net Framework3.5问题 window Server2012 安装数据库的时候可能会报缺少.net Framework3.5,需要去服务管理器添加.net F ...