简介

Windows在线程控制方面提供了多种信号处理机制,其中一种便是使用 CreateEvent() 函数创建事件,然后使用信号控制线程运行。其中将事件变为有信号可使用 SetEvent() 函数,将事件信号复位(变为无信号)可使用 ResetEvent() 函数,信号可以配合 WaitForSingleObject() 函数对线程的同步进行控制,当有信号时,此函数便会放行;无信号时,此函数会将阻塞。

提示: CreateEvent() 函数的参数 bManualReset 的含义是信号是否由人工复位,如果选择true,则信号必须手动采用ResetEvent() 函数进行复位操作。在这种情况下,可能会偶尔出现线程不同步的情况,问题出在可能同时会有多个线程穿过 WaitForSingleObject() 函数,导致复位失效,所以在这种情况下,为确保万无一失,我们一般会再添加一个限制条件,例如临界区互斥体;如果选择的是false,则当一个信号经过 WaitForSingleObject() 函数的时候,函数会自动将事件信号复位。

代码样例

  • bManualReset参数为 false
  1. ////////////////////////////////
  2. //
  3. // FileName : ThreadEventDemo.cpp
  4. // Creator : PeterZheng
  5. // Date : 2018/9/23 18:00
  6. // Comment : The usage of "CreateEvent"
  7. //
  8. ////////////////////////////////
  9. #pragma once
  10. #include <cstdio>
  11. #include <iostream>
  12. #include <cstdlib>
  13. #include <windows.h>
  14. using namespace std;
  15. DWORD WINAPI func1(LPVOID lpParam);
  16. DWORD WINAPI func2(LPVOID lpParam);
  17. HANDLE hEvent = NULL;
  18. unsigned int unCount = 0;
  19. DWORD WINAPI func1(LPVOID lpParam)
  20. {
  21. while (true)
  22. {
  23. WaitForSingleObject(hEvent, INFINITE);
  24. if (unCount < 100)
  25. {
  26. unCount++;
  27. Sleep(10);
  28. cout << "Count: " << unCount << endl;
  29. SetEvent(hEvent);
  30. continue;
  31. }
  32. // 因为WaitForSingleObject函数会自动复位,可能导致另外一个线程始终等待不到信号,造成“假死”现象,所以这里需要使用SetEvent重置信号。
  33. SetEvent(hEvent);
  34. break;
  35. }
  36. return 0;
  37. }
  38. DWORD WINAPI func2(LPVOID lpParam)
  39. {
  40. while (true)
  41. {
  42. WaitForSingleObject(hEvent, INFINITE);
  43. if (unCount < 100)
  44. {
  45. unCount++;
  46. Sleep(10);
  47. cout << "Count: " << unCount << endl;
  48. SetEvent(hEvent); // 设置事件为有信号状态
  49. continue;
  50. }
  51. SetEvent(hEvent);
  52. break;
  53. }
  54. return 0;
  55. }
  56. int main(void)
  57. {
  58. HANDLE hThread[2] = { NULL };
  59. hEvent = CreateEvent(NULL, false, false, NULL); //创建一个匿名事件,当参数bManualReset设置为false时
  60. hThread[0] = CreateThread(NULL, 0, func1, NULL, 0, NULL);
  61. cout << "Thread-1 is RUNNING" << endl;
  62. hThread[1] = CreateThread(NULL, 0, func2, NULL, 0, NULL);
  63. cout << "Thread-2 is RUNNING" << endl;
  64. SetEvent(hEvent);
  65. WaitForMultipleObjects(2, hThread, true, INFINITE); //等待两个线程运行结束
  66. CloseHandle(hThread[0]);
  67. CloseHandle(hThread[1]);
  68. CloseHandle(hEvent);
  69. system("pause");
  70. return 0;
  71. }
  • bManualReset参数为 true
  1. ////////////////////////////////
  2. //
  3. // FileName : ThreadEventDemo.cpp
  4. // Creator : PeterZheng
  5. // Date : 2018/9/23 18:00
  6. // Comment : The usage of "CreateEvent"
  7. //
  8. ////////////////////////////////
  9. #pragma once
  10. #include <cstdio>
  11. #include <iostream>
  12. #include <cstdlib>
  13. #include <windows.h>
  14. using namespace std;
  15. DWORD WINAPI func1(LPVOID lpParam);
  16. DWORD WINAPI func2(LPVOID lpParam);
  17. HANDLE hEvent = NULL;
  18. HANDLE hMutex = NULL;
  19. unsigned int unCount = 0;
  20. DWORD WINAPI func1(LPVOID lpParam)
  21. {
  22. while (true)
  23. {
  24. WaitForSingleObject(hEvent, INFINITE);
  25. WaitForSingleObject(hMutex, INFINITE); //为互斥体上锁
  26. ResetEvent(hEvent); // 重置事件为无信号状态
  27. if (unCount < 100)
  28. {
  29. unCount++;
  30. Sleep(10);
  31. cout << "Count: " << unCount << endl;
  32. SetEvent(hEvent); // 设置事件为有信号状态
  33. ReleaseMutex(hMutex); //互斥体解锁
  34. }
  35. else
  36. {
  37. SetEvent(hEvent);
  38. ReleaseMutex(hMutex);
  39. break;
  40. }
  41. }
  42. return 0;
  43. }
  44. DWORD WINAPI func2(LPVOID lpParam)
  45. {
  46. while (true)
  47. {
  48. WaitForSingleObject(hEvent, INFINITE);
  49. WaitForSingleObject(hMutex, INFINITE); //为互斥体上锁
  50. ResetEvent(hEvent); // 重置事件为无信号状态
  51. if (unCount < 100)
  52. {
  53. unCount++;
  54. Sleep(10);
  55. cout << "Count: " << unCount << endl;
  56. SetEvent(hEvent); // 设置事件为有信号状态
  57. ReleaseMutex(hMutex);
  58. }
  59. else
  60. {
  61. SetEvent(hEvent);
  62. ReleaseMutex(hMutex);
  63. break;
  64. }
  65. }
  66. return 0;
  67. }
  68. int main(void)
  69. {
  70. HANDLE hThread[2] = { NULL };
  71. hEvent = CreateEvent(NULL, true, false, NULL); //创建一个匿名事件,当参数bManualReset设置为true时
  72. hMutex = CreateMutex(NULL, false, NULL); //创建一个匿名互斥体
  73. hThread[0] = CreateThread(NULL, 0, func1, NULL, 0, NULL);
  74. cout << "Thread-1 is RUNNING" << endl;
  75. hThread[1] = CreateThread(NULL, 0, func2, NULL, 0, NULL);
  76. cout << "Thread-2 is RUNNING" << endl;
  77. SetEvent(hEvent); // 设置事件为有信号状态
  78. WaitForMultipleObjects(2, hThread, true, INFINITE); //等待两个线程运行结束
  79. CloseHandle(hThread[0]);
  80. CloseHandle(hThread[1]);
  81. CloseHandle(hEvent);
  82. CloseHandle(hMutex);
  83. system("pause");
  84. return 0;
  85. }

参考文档

【1】https://blog.csdn.net/s_lisheng/article/details/74278765

C++多线程同步技巧(二)--- 事件的更多相关文章

  1. Windows多线程同步系列之三-----事件对象

    事件是一个内核事件,内核事件是什么呢,我理解也不深入也不好说,暂且理解为一个内核维护的数据类型吧通过内核事件同步主要 的方法是对事件的信号有和无来进行同步. 比如当我们一个线程进入一段临界代码(独占代 ...

  2. C++多线程同步技巧(四)--- 信号量

    简介 信号量是维护0到指定最大值之间的同步对象.信号量状态在其计数大于0时是有信号的,而其计数是0时是无信号的.信号量对象在控制上可以支持有限数量共享资源的访问,可以用于线程同步,预防死锁等领域. 信 ...

  3. C++多线程同步技巧(三)--- 互斥体

    简介 Windows互斥对象机制. 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问,在线程同步与保证程序单体运行上都有相当大的用处. 代码 ...

  4. C++多线程同步技巧(一) --- 临界区

    简介 C++中关于多线程的内容对于构建工程来说是至关重要的,C++本身也对关于多线程的操作提供了很好的支持.本章笔者就来介绍一下C++有关于多线程的重要知识点---临界区. 临界区的作用 线程就像是进 ...

  5. [一个经典的多线程同步问题]解决方案二:Event事件

    使用关键段来解决经典的多线程同步互斥问题,由于关键段的“线程所有权”特性所以关键段只能用于线程的互斥而不能用于同步.本篇介绍用事件Event来尝试解决这个线程同步问题. 首先介绍下如何使用事件.事件E ...

  6. C#多线程同步事件及等待句柄AutoResetEvent 和 ManualResetEvent

    最近捣鼓了一下多线程的同步问题,发现其实C#关于多线程同步事件处理还是很灵活,这里主要写一下,自己测试的一些代码,涉及到了AutoResetEvent 和 ManualResetEvent,当然还有也 ...

  7. Windows多线程同步系列之二-----关键区

    关键区对象为:CRITICAL_SECTION 当某个线程进入关键区之后,其他线程将阻塞等待,知道该线程释放关键区的拥有权. 关键区同步主要有以下几个API 初始化关键区对象,无返回值,传入一个关键区 ...

  8. java多线程同步以及线程间通信详解&消费者生产者模式&死锁&Thread.join()(多线程编程之二)

    本篇我们将讨论以下知识点: 1.线程同步问题的产生 什么是线程同步问题,我们先来看一段卖票系统的代码,然后再分析这个问题: package com.zejian.test; /** * @author ...

  9. java多线程同步机制

    一.关键字: thread(线程).thread-safe(线程安全).intercurrent(并发的) synchronized(同步的).asynchronized(异步的). volatile ...

随机推荐

  1. Java——构造方法和匿名对象

    前言 在编写程序时不安全的初始化会导致程序发生发生重大错误.为了使程序可以被安全地初始化,C++引入了构造器(也可以成为构造方法)的概念,这是一个在创建对象时被自动调用的特殊方法.Java中也采用了构 ...

  2. Hystrix降级逻辑中如何获取触发的异常

    通过之前Spring Cloud系列教程中的<Spring Cloud构建微服务架构:服务容错保护(Hystrix服务降级)>一文,我们已经知道如何通过Hystrix来保护自己的服务不被外 ...

  3. SpringBoot系列——WebSocket

    关于websocket的介绍与实现,我之前写过一篇博客,记录了用springboot-websocket实现了私聊.群聊的简单实例,这里就只提供一个入口,不再重复的写了,WebSocket+Java ...

  4. tomcat和jdk版本兼容(Tomcat版本要比jdk高)

    用的tomcat是低版本的,但是用的jdk却是高版本的,用Servlet做的项目运行都没有问题,但是直接运行jsp却死活都运行失败. 最后发现是tomcat和jdk的版本问题造成的. 总结如下: to ...

  5. 从零开始学安全(五)●Vmware虚拟机三种网络模式详解

    vmware为我们提供了三种网络工作模式,它们分别是:Bridged(桥接模式).NAT(网络地址转换模式).Host-Only(仅主机模式). NAT(网络地址转换模式) NAT(网络地址转换)vm ...

  6. C#中设置窗口在最前显示而其他窗口不能使用

    对程序中的一个Form1处理,让其在打开的情况下,其他窗体都无法使用,全部焦点都在他身上. 1.这种方法最简单,直接调用窗体的模态对话框函数显示窗体 Form1.ShowDialog(): 2.设置窗 ...

  7. C# 插入条码到Excel指定单元格

    .NET中Barcode Library的应用二 介绍 在上一篇中我已经简单介绍了这个函数库(条形码应用之一------------函数库的简介).在这一篇中我将使用这个库提供更多的操作,希望对大家有 ...

  8. Linux-read 命令(20)

    Linux read 命令 参数说明: -a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符. -d 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志. -p ...

  9. Java学习笔记之——Object类

    所有类的祖先 如果一个类没有显式继承,则继承Object 每一个类都直接或间接的是Object的子类 相关API: protected Objectclone() 创建并返回此对象的副本. boole ...

  10. JavaWeb学习日记----DTD

    DTD:文档类型定义,可以定义合法的XML文档构建模块.使用一系列的合法标签元素来定义文档的结构. 现有一个XML文档内容如下: <?xml version="1.0"?&g ...