简单介绍

如果预计操作的等待的时间非常短,可以考虑使用轻量级的手动重置事件,ManualResetEventSlim。它可以发出信号和等待事件。从名称和使用方式上看,它主要是提供以人为本的操作方式,在基于人对程序运行过程非常了解的情况下,由人控制整个同步的过程。

ManualResetEventSlim 提供了3个常用的方法和3个只读的属性。

构造函数:

  • ManualResetEventSlim():使用无信号初始状态初始化 ManualResetEventSlim 类的新实例。
  • ManualResetEventSlim(Boolean):使用一个指示是否将初始状态设置为有信号的布尔值初始化 
    ManualResetEventSlim 类的新实例。
  • ManualResetEventSlim(Boolean, Int32):使用一个指示是否将初始状态设置为有信号和指定自旋计数的布尔值初始化 ManualResetEventSlim 类的新实例。

方法:

  • Reset:将事件置为false (取消设置/取消信号)
  • Set:将事件置为true(设置/发出信号),如果有任务在等待,这时它会得到这个信号并解除阻塞。
  • Wait:阻塞当前任务或线程,直到另外的线程发出信号。

属性:

  • IsSet:一个bool值,表明事件是否被设置。
  • SpinCount:进入内核等待前要执行自旋的次数。
  • WaitHandle:提供了操作系统对象WaitHandle的访问。通过这个对象可以等待对共享资源的排他访问。

程序示例:在这个例子中使用了ManualResetEventSlim,它使得Task1,2,3变成了顺序运行。

  1. using System;
  2. using System.Text;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. namespace Sample5_7_manualreseteventslim
  6. {
  7. class Program
  8. {
  9. private static int _TaskNum = 3;
  10. private static Task[] _Tasks;
  11. private static StringBuilder _StrBlder;
  12. private const int RUN_LOOP = 10;
  13. private static ManualResetEventSlim m_Worker2Event;
  14. private static ManualResetEventSlim m_Worker3Event;
  15. private static void Work1(int TaskID)
  16. {
  17. int i = 0;
  18. string log = "";
  19. while (i < RUN_LOOP)
  20. {
  21. log = String.Format("Time: {0} Task : #{1} Value: {2} =====\n",
  22. DateTime.Now.TimeOfDay, TaskID, i);
  23. i++;
  24. try
  25. {
  26. _StrBlder.Append(log);
  27. }
  28. finally
  29. {
  30. m_Worker2Event.Set();
  31. }
  32. }
  33. }
  34. private static void Work2(int TaskID)
  35. {
  36. int i = 0;
  37. string log = "";
  38. m_Worker2Event.Wait();
  39. while ((i < RUN_LOOP) && (m_Worker2Event.IsSet))
  40. {
  41. log = String.Format("Time: {0} Task : #{1} Value: {2} *****\n",
  42. DateTime.Now.TimeOfDay, TaskID, i);
  43. i++;
  44. try
  45. {
  46. _StrBlder.Append(log);
  47. }
  48. finally
  49. {
  50. m_Worker3Event.Set();
  51. }
  52. }
  53. }
  54. private static void Work3(int TaskID)
  55. {
  56. int i = 0;
  57. string log = "";
  58. m_Worker3Event.Wait();
  59. while ((i < RUN_LOOP) && (m_Worker3Event.IsSet))
  60. {
  61. log = String.Format("Time: {0} Task : #{1} Value: {2} ~~~~~\n",
  62. DateTime.Now.TimeOfDay, TaskID, i);
  63. i++;
  64. try
  65. {
  66. _StrBlder.Append(log);
  67. }
  68. finally
  69. {
  70. }
  71. }
  72. }
  73. static void Main(string[] args)
  74. {
  75. _Tasks = new Task[_TaskNum];
  76. _StrBlder = new StringBuilder();
  77. m_Worker2Event = new ManualResetEventSlim(false, 100);
  78. m_Worker3Event = new ManualResetEventSlim(false, 100);
  79. _Tasks[0] = Task.Factory.StartNew((num) =>
  80. {
  81. var taskid = (int)num;
  82. Work1(taskid);
  83. }, 0);
  84. _Tasks[1] = Task.Factory.StartNew((num) =>
  85. {
  86. var taskid = (int)num;
  87. Work2(taskid);
  88. }, 1);
  89. _Tasks[2] = Task.Factory.StartNew((num) =>
  90. {
  91. var taskid = (int)num;
  92. Work3(taskid);
  93. }, 2);
  94. var finalTask = Task.Factory.ContinueWhenAll(_Tasks, (tasks) =>
  95. {
  96. Task.WaitAll(_Tasks);
  97. Console.WriteLine("==========================================================");
  98. Console.WriteLine("All Phase is completed");
  99. Console.WriteLine("==========================================================");
  100. Console.WriteLine(_StrBlder);
  101. });
  102. try
  103. {
  104. finalTask.Wait();
  105. }
  106. catch (AggregateException aex)
  107. {
  108. Console.WriteLine("Task failed And Canceled" + aex.ToString());
  109. }
  110. finally
  111. {
  112. m_Worker2Event.Dispose();
  113. m_Worker3Event.Dispose();
  114. }
  115. Console.ReadLine();
  116. }
  117. }
  118. }

使用超时和取消

超时机制对于任务的同步是非常必要的,这里也提供了用户设置超时的方法。 
ManualResetEventSilm.Wait(int TIME_OUT);

程序示例:在这个例子中任务1会等待5秒后在设置Event,但任务2,3的超时时间为2秒。

  1. using System;
  2. using System.Text;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. namespace Sample5_7_manualreseteventslim
  6. {
  7. class Program
  8. {
  9. private static int _TaskNum = 3;
  10. private static Task[] _Tasks;
  11. private static StringBuilder _StrBlder;
  12. private const int RUN_LOOP = 10;
  13. private static ManualResetEventSlim m_Worker2Event;
  14. private static ManualResetEventSlim m_Worker3Event;
  15. private static void Work1(int TaskID)
  16. {
  17. int i = 0;
  18. string log = "";
  19. while (i < RUN_LOOP)
  20. {
  21. log = String.Format("Time: {0} Task : #{1} Value: {2} =====\n",
  22. DateTime.Now.TimeOfDay, TaskID, i);
  23. i++;
  24. try
  25. {
  26. _StrBlder.Append(log);
  27. }
  28. finally
  29. {
  30. System.Threading.Thread.Sleep(5000);
  31. m_Worker2Event.Set();
  32. }
  33. }
  34. }
  35. private static void Work2(int TaskID)
  36. {
  37. int i = 0;
  38. string log = "";
  39. if (!m_Worker2Event.Wait(2000))
  40. {
  41. Console.WriteLine("Task 2 wait for event TIME OUT!!");
  42. return;
  43. }
  44. while ((i < RUN_LOOP) && (m_Worker2Event.IsSet))
  45. {
  46. log = String.Format("Time: {0} Task : #{1} Value: {2} *****\n",
  47. DateTime.Now.TimeOfDay, TaskID, i);
  48. i++;
  49. try
  50. {
  51. _StrBlder.Append(log);
  52. }
  53. finally
  54. {
  55. m_Worker3Event.Set();
  56. }
  57. }
  58. }
  59. private static void Work3(int TaskID)
  60. {
  61. int i = 0;
  62. string log = "";
  63. if (!m_Worker3Event.Wait(2000))
  64. {
  65. Console.WriteLine("Task 3 wait for event TIME OUT!!");
  66. return;
  67. }
  68. while ((i < RUN_LOOP) && (m_Worker3Event.IsSet))
  69. {
  70. log = String.Format("Time: {0} Task : #{1} Value: {2} ~~~~~\n",
  71. DateTime.Now.TimeOfDay, TaskID, i);
  72. i++;
  73. try
  74. {
  75. _StrBlder.Append(log);
  76. }
  77. finally
  78. {
  79. }
  80. }
  81. }
  82. static void Main(string[] args)
  83. {
  84. _Tasks = new Task[_TaskNum];
  85. _StrBlder = new StringBuilder();
  86. m_Worker2Event = new ManualResetEventSlim(false, 100);
  87. m_Worker3Event = new ManualResetEventSlim(false, 100);
  88. _Tasks[0] = Task.Factory.StartNew((num) =>
  89. {
  90. var taskid = (int)num;
  91. Work1(taskid);
  92. }, 0);
  93. _Tasks[1] = Task.Factory.StartNew((num) =>
  94. {
  95. var taskid = (int)num;
  96. Work2(taskid);
  97. }, 1);
  98. _Tasks[2] = Task.Factory.StartNew((num) =>
  99. {
  100. var taskid = (int)num;
  101. Work3(taskid);
  102. }, 2);
  103. var finalTask = Task.Factory.ContinueWhenAll(_Tasks, (tasks) =>
  104. {
  105. Task.WaitAll(_Tasks);
  106. Console.WriteLine("==========================================================");
  107. Console.WriteLine("All Phase is completed");
  108. Console.WriteLine("==========================================================");
  109. Console.WriteLine(_StrBlder);
  110. });
  111. try
  112. {
  113. finalTask.Wait();
  114. }
  115. catch (AggregateException aex)
  116. {
  117. Console.WriteLine("Task failed And Canceled" + aex.ToString());
  118. }
  119. finally
  120. {
  121. m_Worker2Event.Dispose();
  122. m_Worker3Event.Dispose();
  123. }
  124. Console.ReadLine();
  125. }
  126. }
  127. }

可以注意到其实Task 1只是阻塞了Task2,但Task3也受到了超时的影响。超时机制在系统中的传播会对整个程序造成一定的影响。

好的方面:帮助系统所有模块了解到系统中出现了某些异常,要采取措施了。 
坏的方面:有些模块对于其他模块的超时并没有相应的准备,直接导致一连串的异常的反应,系统崩溃。

跨进程或AppDomain的同步

如果需要实现跨进程的同步,这时可以使用ManualResetEvent。ManualResetEvent和ManualResetEventSlim有一定的区别,比如它没有IsSet属性。

方法:

  • Reset:将事件置为false (取消设置/取消信号)
  • Set:将事件置为true(设置/发出信号),如果有任务在等待,这时它会得到这个信号并解除阻塞。
  • WaitOne:阻塞当前任务或线程,直到另外的线程发出信号。

ManualResetEvent的使用方式和ManualResetEventSlim还是基本一致的。

C# 并行编程 之 轻量级手动重置事件的使用的更多相关文章

  1. C#线程同步手动重置事件——ManualResetEvent

    和AutoResetEvent类的区别是,Manual一旦set后不会自动reset,会放行所有waitone的线程,而autoresetevent每一次set之后只会放行一个waitone的线程,然 ...

  2. C#并行编程-线程同步原语

    菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 ...

  3. C#并行编程 (Barrier,CountdownEvent,ManualResetEventSlim,SemaphoreSlim,SpinLock,SpinWait )

    背景 有时候必须访问变量.实例.方法.属性或者结构体,而这些并没有准备好用于并发访问,或者有时候需要执行部分代码,而这些代码必须单独运行,这是不得不通过将任务分解的方式让它们独立运行. 当任务和线程要 ...

  4. C#并行编程(6):线程同步面面观

    理解线程同步 线程的数据访问 在并行(多线程)环境中,不可避免地会存在多个线程同时访问某个数据的情况.多个线程对共享数据的访问有下面3种情形: 多个线程同时读取数据: 单个线程更新数据,此时其他线程读 ...

  5. C#中的多线程 - 并行编程 z

    原文:http://www.albahari.com/threading/part5.aspx 专题:C#中的多线程 1并行编程Permalink 在这一部分,我们讨论 Framework 4.0 加 ...

  6. 【温故而知新-万花筒】C# 异步编程 逆变 协变 委托 事件 事件参数 迭代 线程、多线程、线程池、后台线程

    额基本脱离了2.0 3.5的时代了.在.net 4.0+ 时代.一切都是辣么简单! 参考文档: http://www.cnblogs.com/linzheng/archive/2012/04/11/2 ...

  7. 6.跑步者--并行编程框架 ForkJoin

    本文如果您已经了解一般并行编程知识.了解Java concurrent部分如ExecutorService等相关内容. 虽说是Java的ForkJoin并行框架.但不要太在意Java,当中的思想在其他 ...

  8. Python3 系列之 并行编程

    进程和线程 进程是程序运行的实例.一个进程里面可以包含多个线程,因此同一进程下的多个线程之间可以共享线程内的所有资源,它是操作系统动态运行的基本单元:每一个线程是进程下的一个实例,可以动态调度和独立运 ...

  9. Objective-C编程 — 并行编程

    多线程 线程的基本概念 线程 (thread)是进程(process)A 内假想的持有 CPU 使用权的执行单位.一般情况下,一个进程 只有一个线程,但也可以创建多个线程并在进程中并行执行.应用在执行 ...

随机推荐

  1. ASP.NET MVC WEBAPI第一次接触

    asp.net 的MVC4 WEBAPI的出现已经有段时间了.最近因为做自己的一些小玩儿,要做一个API,正好可以学习一下这个WEBAPI. WEBAPI项目的创建我就不啰嗦,先来看看webapi的路 ...

  2. Vagrant的一个BUG - 不支持'change_host_name'

    ==> master: Setting hostname... Vagrant attempted to execute the capability 'change_host_name' on ...

  3. Nodejs学习(一)-Nodejs和express的安装和配置

    声明我的操作系统是ubuntu环境,直接下载了tar文件进行解压安装,步骤如下 1.nodejs官网下载node-v4.6.0-linux-x86.tar.xz文件. youyuan1980@youy ...

  4. webForm中的验证控件

    1.非空验证控件:RequireFieldValidator  :2.数据比较验证:CompareValidator :3.数据范围验证:RangeValidator :4.正则表达式验证:Regul ...

  5. 软件工程练习, 模块化,单元测试,回归测试,TDD

    这是<构建之法>实战教学的一部分.适合作为同学们的第二个程序作业. 第一个程序作业: 请看 “概论” 一章的练习,或者老师的题目,例如这个. 作业要求: 软件工程的作业越来越有意思了, 我 ...

  6. python基础知识9——模块2——常见内置模块

    内置模块 内置模块是Python自带的功能,在使用内置模块相应的功能时,需要[先导入]再[使用] 1.sys 用于提供对Python解释器相关的操作: sys.argv 命令行参数List,第一个元素 ...

  7. Spring AOP /代理模式/事务管理/读写分离/多数据源管理

    参考文章: http://www.cnblogs.com/MOBIN/p/5597215.html http://www.cnblogs.com/fenglie/articles/4097759.ht ...

  8. 在sql server中利用with as实现递归功能

    在sqlserver2005之前,要实现递归功能比较麻烦,比如可能会要用到临时表与while语句来循环.自sqlserver2005之后,新增了with as功能语法,即 公用表达式(CTE),让递归 ...

  9. Badboy使用数据源Excel进行脚本参数化

    1.首先新建一个Excel,这里示例我写得非常简单,由两由数据组成,第一行为表头.见下图: 2.录制脚本,见上一篇,录制一个非常简单的搜狗查询 3.添加数据源,在Tools面板中找到Data Sour ...

  10. 学习C:打印输入中单词长度的水平方向直方图

    #include <stdio.h>#define IN 1#define OUT 0#define MAXWL 16 main() { /*打印输入单词长度的水平直方图*/ int c, ...