VC 线程间通信的三种方式
实现线程间通信的方法有很多,常用的主要是通过全局变量、自定义消息和事件对象等来实现的。其中又以对全局变量的使用最为简洁。该方法将全局变量作为线程监视的对象,并通过在主线程对此变量值的改变而实现对子线程的控制。
由于这里的全局变量需要在使用它的线程之外对其值进行改变,这就需要通过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 线程间通信的三种方式的更多相关文章
- 【转】VC 线程间通信的三种方式
原文网址:http://my.oschina.net/laopiao/blog/94728 1.使用全局变量(窗体不适用) 实现线程间通信的方法有很多,常用的主要是通过全局变量.自定义消息和 ...
- 线程间通信的三种方式(NSThread,GCD,NSOperation)
一.NSThread线程间通信 #import "ViewController.h" @interface ViewController ()<UIScrollViewDel ...
- 容器间通信的三种方式 - 每天5分钟玩转 Docker 容器技术(35)
容器之间可通过 IP,Docker DNS Server 或 joined 容器三种方式通信. IP 通信 从上一节的例子可以得出这样一个结论:两个容器要能通信,必须要有属于同一个网络的网卡. 满足这 ...
- Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
- Java线程间通信-回调的实现方式
Java线程间通信-回调的实现方式 Java线程间通信是非常复杂的问题的.线程间通信问题本质上是如何将与线程相关的变量或者对象传递给别的线程,从而实现交互. 比如举一个简单例子,有一个多线程的 ...
- 多线程之线程间协作的两种方式:wait、notify、notifyAll和Condition
Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
- 19、Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
- [转] 微信小程序页面间通信的5种方式
微信小程序页面间通的5种方式 PageModel(页面模型)对小程序而言是很重要的一个概念,从app.json中也可以看到,小程序就是由一个个页面组成的. 如上图,这是一个常见结构的小程序:首页是一个 ...
- Angular 组件通信的三种方式
我们可以通过以下三种方式来实现: 传递一个组件的引用给另一个组件 通过子组件发送EventEmitter和父组件通信 通过serive通信 1. 传递一个组件的引用给另一个组件 Demo1 模板引用变 ...
随机推荐
- 用宏判断程序当前的运行模式(debug or release)
例子 #if defined (DEBUG) && DEBUG == 1 // 如果当前处于debug 模式 //to do #else //to do #endif
- waitdialogform z
namespace DevExpress.Utils { using DevExpress.LookAndFeel; using DevExpress.Skins; using DevExpress. ...
- DocumentManager 在标签位置显示气泡框 z
关于DevExpress DockManager下的DocumentManager头部标签如何显示气泡框,类似Visual studio那样显示文件的路径,如下图所示,------- 方式很简单,从工 ...
- coursera课程Text Retrieval and Search Engines之Week 1 Overview
Week 1 OverviewHelp Center Week 1 On this page: Instructional Activities Time Goals and Objectives K ...
- 24.Semaphore
Semaphore 在进程方面完成信号线的控制,可以控制某个资源下,可被同时访问的线程个数.对系统的访问量进行评估,信号量维护了一个许可集:在许可前会阻塞每一个 semaphore.acqui ...
- go语言基础之结构体普通变量初始化
1.结构体 1.1.结构体类型 有时我们需要将不同类型的数据组合成一个有机的整体,如:一个学生有学号/姓名/性别/年龄/地址等属性.显然单独定义以上变量比较繁琐,数据不便于管理. 结构体是一种聚合的数 ...
- scala 学习笔记五 foreach, map, reduce
例子 val v = Vector(,,,) ) println(s) //输出:Vector(2, 4, 6, 8) val v2 = Vector(,,,) var v3 = v2.reduce( ...
- ftp 命令行操作 经常使用命令
> ftp <host> [port] > pwd # 查看当前文件夹 > dir # 查看FTPserver中的文件及文件夹 > mkdir <dirn ...
- Back Track 5 之 网络踩点(二)
操作系统探测 Xprobe2 通过ICMP协议来获得指纹,通过模糊矩阵统计分析主动探测数据包对应的ICMP数据特征,进而探测得到远端操作系统的类型. 格式: ./xprobe2 域名 ./xprobe ...
- 【Hibernate】Hibernate3.x独立执行时的Failed to load class "org.slf4j.impl.StaticLoggerBinder"错误
按理说,假设Hibernate不依附于SSH执行,像<[Struts2+Hibernate3+Spring3]利用SSH整合,完毕打印用户表,用户登录.注冊.改动password系统>(点 ...