C++多线程同步技巧(二)--- 事件
简介
Windows在线程控制方面提供了多种信号处理机制,其中一种便是使用 CreateEvent() 函数创建事件,然后使用信号控制线程运行。其中将事件变为有信号可使用 SetEvent() 函数,将事件信号复位(变为无信号)可使用 ResetEvent() 函数,信号可以配合 WaitForSingleObject() 函数对线程的同步进行控制,当有信号时,此函数便会放行;无信号时,此函数会将阻塞。
提示: CreateEvent() 函数的参数 bManualReset 的含义是信号是否由人工复位,如果选择true,则信号必须手动采用ResetEvent() 函数进行复位操作。在这种情况下,可能会偶尔出现线程不同步的情况,问题出在可能同时会有多个线程穿过 WaitForSingleObject() 函数,导致复位失效,所以在这种情况下,为确保万无一失,我们一般会再添加一个限制条件,例如临界区或互斥体;如果选择的是false,则当一个信号经过 WaitForSingleObject() 函数的时候,函数会自动将事件信号复位。
代码样例
- bManualReset参数为 false
////////////////////////////////
//
// FileName : ThreadEventDemo.cpp
// Creator : PeterZheng
// Date : 2018/9/23 18:00
// Comment : The usage of "CreateEvent"
//
////////////////////////////////
#pragma once
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <windows.h>
using namespace std;
DWORD WINAPI func1(LPVOID lpParam);
DWORD WINAPI func2(LPVOID lpParam);
HANDLE hEvent = NULL;
unsigned int unCount = 0;
DWORD WINAPI func1(LPVOID lpParam)
{
while (true)
{
WaitForSingleObject(hEvent, INFINITE);
if (unCount < 100)
{
unCount++;
Sleep(10);
cout << "Count: " << unCount << endl;
SetEvent(hEvent);
continue;
}
// 因为WaitForSingleObject函数会自动复位,可能导致另外一个线程始终等待不到信号,造成“假死”现象,所以这里需要使用SetEvent重置信号。
SetEvent(hEvent);
break;
}
return 0;
}
DWORD WINAPI func2(LPVOID lpParam)
{
while (true)
{
WaitForSingleObject(hEvent, INFINITE);
if (unCount < 100)
{
unCount++;
Sleep(10);
cout << "Count: " << unCount << endl;
SetEvent(hEvent); // 设置事件为有信号状态
continue;
}
SetEvent(hEvent);
break;
}
return 0;
}
int main(void)
{
HANDLE hThread[2] = { NULL };
hEvent = CreateEvent(NULL, false, false, NULL); //创建一个匿名事件,当参数bManualReset设置为false时
hThread[0] = CreateThread(NULL, 0, func1, NULL, 0, NULL);
cout << "Thread-1 is RUNNING" << endl;
hThread[1] = CreateThread(NULL, 0, func2, NULL, 0, NULL);
cout << "Thread-2 is RUNNING" << endl;
SetEvent(hEvent);
WaitForMultipleObjects(2, hThread, true, INFINITE); //等待两个线程运行结束
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
CloseHandle(hEvent);
system("pause");
return 0;
}
- bManualReset参数为 true
////////////////////////////////
//
// FileName : ThreadEventDemo.cpp
// Creator : PeterZheng
// Date : 2018/9/23 18:00
// Comment : The usage of "CreateEvent"
//
////////////////////////////////
#pragma once
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <windows.h>
using namespace std;
DWORD WINAPI func1(LPVOID lpParam);
DWORD WINAPI func2(LPVOID lpParam);
HANDLE hEvent = NULL;
HANDLE hMutex = NULL;
unsigned int unCount = 0;
DWORD WINAPI func1(LPVOID lpParam)
{
while (true)
{
WaitForSingleObject(hEvent, INFINITE);
WaitForSingleObject(hMutex, INFINITE); //为互斥体上锁
ResetEvent(hEvent); // 重置事件为无信号状态
if (unCount < 100)
{
unCount++;
Sleep(10);
cout << "Count: " << unCount << endl;
SetEvent(hEvent); // 设置事件为有信号状态
ReleaseMutex(hMutex); //互斥体解锁
}
else
{
SetEvent(hEvent);
ReleaseMutex(hMutex);
break;
}
}
return 0;
}
DWORD WINAPI func2(LPVOID lpParam)
{
while (true)
{
WaitForSingleObject(hEvent, INFINITE);
WaitForSingleObject(hMutex, INFINITE); //为互斥体上锁
ResetEvent(hEvent); // 重置事件为无信号状态
if (unCount < 100)
{
unCount++;
Sleep(10);
cout << "Count: " << unCount << endl;
SetEvent(hEvent); // 设置事件为有信号状态
ReleaseMutex(hMutex);
}
else
{
SetEvent(hEvent);
ReleaseMutex(hMutex);
break;
}
}
return 0;
}
int main(void)
{
HANDLE hThread[2] = { NULL };
hEvent = CreateEvent(NULL, true, false, NULL); //创建一个匿名事件,当参数bManualReset设置为true时
hMutex = CreateMutex(NULL, false, NULL); //创建一个匿名互斥体
hThread[0] = CreateThread(NULL, 0, func1, NULL, 0, NULL);
cout << "Thread-1 is RUNNING" << endl;
hThread[1] = CreateThread(NULL, 0, func2, NULL, 0, NULL);
cout << "Thread-2 is RUNNING" << endl;
SetEvent(hEvent); // 设置事件为有信号状态
WaitForMultipleObjects(2, hThread, true, INFINITE); //等待两个线程运行结束
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
CloseHandle(hEvent);
CloseHandle(hMutex);
system("pause");
return 0;
}
参考文档
【1】https://blog.csdn.net/s_lisheng/article/details/74278765
C++多线程同步技巧(二)--- 事件的更多相关文章
- Windows多线程同步系列之三-----事件对象
事件是一个内核事件,内核事件是什么呢,我理解也不深入也不好说,暂且理解为一个内核维护的数据类型吧通过内核事件同步主要 的方法是对事件的信号有和无来进行同步. 比如当我们一个线程进入一段临界代码(独占代 ...
- C++多线程同步技巧(四)--- 信号量
简介 信号量是维护0到指定最大值之间的同步对象.信号量状态在其计数大于0时是有信号的,而其计数是0时是无信号的.信号量对象在控制上可以支持有限数量共享资源的访问,可以用于线程同步,预防死锁等领域. 信 ...
- C++多线程同步技巧(三)--- 互斥体
简介 Windows互斥对象机制. 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问,在线程同步与保证程序单体运行上都有相当大的用处. 代码 ...
- C++多线程同步技巧(一) --- 临界区
简介 C++中关于多线程的内容对于构建工程来说是至关重要的,C++本身也对关于多线程的操作提供了很好的支持.本章笔者就来介绍一下C++有关于多线程的重要知识点---临界区. 临界区的作用 线程就像是进 ...
- [一个经典的多线程同步问题]解决方案二:Event事件
使用关键段来解决经典的多线程同步互斥问题,由于关键段的“线程所有权”特性所以关键段只能用于线程的互斥而不能用于同步.本篇介绍用事件Event来尝试解决这个线程同步问题. 首先介绍下如何使用事件.事件E ...
- C#多线程同步事件及等待句柄AutoResetEvent 和 ManualResetEvent
最近捣鼓了一下多线程的同步问题,发现其实C#关于多线程同步事件处理还是很灵活,这里主要写一下,自己测试的一些代码,涉及到了AutoResetEvent 和 ManualResetEvent,当然还有也 ...
- Windows多线程同步系列之二-----关键区
关键区对象为:CRITICAL_SECTION 当某个线程进入关键区之后,其他线程将阻塞等待,知道该线程释放关键区的拥有权. 关键区同步主要有以下几个API 初始化关键区对象,无返回值,传入一个关键区 ...
- java多线程同步以及线程间通信详解&消费者生产者模式&死锁&Thread.join()(多线程编程之二)
本篇我们将讨论以下知识点: 1.线程同步问题的产生 什么是线程同步问题,我们先来看一段卖票系统的代码,然后再分析这个问题: package com.zejian.test; /** * @author ...
- java多线程同步机制
一.关键字: thread(线程).thread-safe(线程安全).intercurrent(并发的) synchronized(同步的).asynchronized(异步的). volatile ...
随机推荐
- HTTP协议学习(一)
一.HTTP报文的组成 请求报文由 请求行.请求头.请求空行.请求实体四部分组成.其中,请求行和请求头共同组成 请求报文头部 请求行:一行,依次由 请求方法.URI(或者应该说是域名?).HTTP协议 ...
- Perl文件句柄引用
目前还没介绍Perl的面向对象,所以这节内容除了几个注意点,没什么可讲的. 以前经常使用大写字母的句柄方式(即所谓的裸字文件句柄,bareword filehandle),现在可以考虑转向使用变量文件 ...
- Office 365平台及其价值主张
重要提示:<Office 365开发入门指南>视频教程还没有上架,目前会先公开几个小节的文字版本,让大家先睹为快,请大家转发给感兴趣的朋友,敬请留意课程的正式上架和优惠券发放通知. 从今天 ...
- python之turtle简单绘制学习
一.方法 1.forward() | fd():向前移动指定的距离.参数:一个数字(integer or float)). turtle.forward(25) 2.backward() | bk() ...
- [android] 采用aidl绑定远程服务
aidl:android interface definition language 安卓接口定义语言 在两个不同的应用程序里面使用同一个接口 使用场景:调用支付宝服务进行支付 先写远程服务端Seri ...
- springMVC_05结果跳转方式
一.总结 总共有四个, 1.设置ModelAndView的值,根据view和视图解析器跳转到指定的页面 2.通过servletapi对象来实现,不需要视图解析器 3.通过springmvc来实现转发和 ...
- python进程基础
目录 进程以及状态 1. 进程 2. 进程的状态 进程的创建-multiprocessing 1. 创建进程 2. 进程pid 3. Process语法结构如下 4. 给子进程指定的函数传递参数 5. ...
- TCP连接与释放
TCP连接的建立 三次握手 TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了LISTEN(监听)状态. TCP客户进程也是先创建传输控制块TCB,然后向服务器 ...
- Java并发编程-ReentrantReadWriteLock
基于AQS的前世今生,来学习并发工具类ReentrantReadWriteLock.本文将从ReentrantReadWriteLock的产生背景.源码原理解析和应用来学习这个并发工具类. 1. 产生 ...
- Go开发之路 -- strings以及strconv的使用
strings的使用 HasPrefix 语法: strings.HasPrefix(s, prefix string) bool // 判断字符串s是否以prefix开头 // 判断一个url是否以 ...