简单介绍

如果预计操作的等待的时间非常短,可以考虑使用轻量级的手动重置事件,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变成了顺序运行。

using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace Sample5_7_manualreseteventslim
{
class Program
{
private static int _TaskNum = 3;
private static Task[] _Tasks;
private static StringBuilder _StrBlder;
private const int RUN_LOOP = 10; private static ManualResetEventSlim m_Worker2Event;
private static ManualResetEventSlim m_Worker3Event; private static void Work1(int TaskID)
{
int i = 0;
string log = ""; while (i < RUN_LOOP)
{
log = String.Format("Time: {0} Task : #{1} Value: {2} =====\n",
DateTime.Now.TimeOfDay, TaskID, i);
i++;
try
{
_StrBlder.Append(log);
}
finally
{
m_Worker2Event.Set();
}
}
} private static void Work2(int TaskID)
{
int i = 0;
string log = ""; m_Worker2Event.Wait(); while ((i < RUN_LOOP) && (m_Worker2Event.IsSet))
{
log = String.Format("Time: {0} Task : #{1} Value: {2} *****\n",
DateTime.Now.TimeOfDay, TaskID, i);
i++;
try
{
_StrBlder.Append(log);
}
finally
{
m_Worker3Event.Set();
}
}
} private static void Work3(int TaskID)
{
int i = 0;
string log = ""; m_Worker3Event.Wait(); while ((i < RUN_LOOP) && (m_Worker3Event.IsSet))
{
log = String.Format("Time: {0} Task : #{1} Value: {2} ~~~~~\n",
DateTime.Now.TimeOfDay, TaskID, i);
i++;
try
{
_StrBlder.Append(log);
}
finally
{
}
}
} static void Main(string[] args)
{
_Tasks = new Task[_TaskNum];
_StrBlder = new StringBuilder();
m_Worker2Event = new ManualResetEventSlim(false, 100);
m_Worker3Event = new ManualResetEventSlim(false, 100); _Tasks[0] = Task.Factory.StartNew((num) =>
{
var taskid = (int)num;
Work1(taskid);
}, 0); _Tasks[1] = Task.Factory.StartNew((num) =>
{
var taskid = (int)num;
Work2(taskid);
}, 1); _Tasks[2] = Task.Factory.StartNew((num) =>
{
var taskid = (int)num;
Work3(taskid);
}, 2); var finalTask = Task.Factory.ContinueWhenAll(_Tasks, (tasks) =>
{
Task.WaitAll(_Tasks);
Console.WriteLine("==========================================================");
Console.WriteLine("All Phase is completed");
Console.WriteLine("==========================================================");
Console.WriteLine(_StrBlder);
}); try
{
finalTask.Wait();
}
catch (AggregateException aex)
{
Console.WriteLine("Task failed And Canceled" + aex.ToString());
}
finally
{
m_Worker2Event.Dispose();
m_Worker3Event.Dispose();
}
Console.ReadLine();
} }
}

使用超时和取消

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

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

using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace Sample5_7_manualreseteventslim
{
class Program
{
private static int _TaskNum = 3;
private static Task[] _Tasks;
private static StringBuilder _StrBlder;
private const int RUN_LOOP = 10; private static ManualResetEventSlim m_Worker2Event;
private static ManualResetEventSlim m_Worker3Event; private static void Work1(int TaskID)
{
int i = 0;
string log = ""; while (i < RUN_LOOP)
{
log = String.Format("Time: {0} Task : #{1} Value: {2} =====\n",
DateTime.Now.TimeOfDay, TaskID, i);
i++;
try
{
_StrBlder.Append(log);
}
finally
{
System.Threading.Thread.Sleep(5000);
m_Worker2Event.Set();
}
}
} private static void Work2(int TaskID)
{
int i = 0;
string log = ""; if (!m_Worker2Event.Wait(2000))
{
Console.WriteLine("Task 2 wait for event TIME OUT!!");
return;
} while ((i < RUN_LOOP) && (m_Worker2Event.IsSet))
{
log = String.Format("Time: {0} Task : #{1} Value: {2} *****\n",
DateTime.Now.TimeOfDay, TaskID, i);
i++;
try
{
_StrBlder.Append(log);
}
finally
{
m_Worker3Event.Set();
}
}
} private static void Work3(int TaskID)
{
int i = 0;
string log = ""; if (!m_Worker3Event.Wait(2000))
{
Console.WriteLine("Task 3 wait for event TIME OUT!!");
return;
} while ((i < RUN_LOOP) && (m_Worker3Event.IsSet))
{
log = String.Format("Time: {0} Task : #{1} Value: {2} ~~~~~\n",
DateTime.Now.TimeOfDay, TaskID, i);
i++;
try
{
_StrBlder.Append(log);
}
finally
{
}
}
} static void Main(string[] args)
{
_Tasks = new Task[_TaskNum];
_StrBlder = new StringBuilder();
m_Worker2Event = new ManualResetEventSlim(false, 100);
m_Worker3Event = new ManualResetEventSlim(false, 100); _Tasks[0] = Task.Factory.StartNew((num) =>
{
var taskid = (int)num;
Work1(taskid);
}, 0); _Tasks[1] = Task.Factory.StartNew((num) =>
{
var taskid = (int)num;
Work2(taskid);
}, 1); _Tasks[2] = Task.Factory.StartNew((num) =>
{
var taskid = (int)num;
Work3(taskid);
}, 2); var finalTask = Task.Factory.ContinueWhenAll(_Tasks, (tasks) =>
{
Task.WaitAll(_Tasks);
Console.WriteLine("==========================================================");
Console.WriteLine("All Phase is completed");
Console.WriteLine("==========================================================");
Console.WriteLine(_StrBlder);
}); try
{
finalTask.Wait();
}
catch (AggregateException aex)
{
Console.WriteLine("Task failed And Canceled" + aex.ToString());
}
finally
{
m_Worker2Event.Dispose();
m_Worker3Event.Dispose();
}
Console.ReadLine();
} }
}

可以注意到其实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. declaration expected but identifier 'firedac' found

    老是出现这种错误,原来是//注解时出来标点错误!

  2. Java的版本分类

    J2EE (JavaEE)Java2 Enterprise Edition定位在服务器端的应用 J2SE(JavaSE)Java2 Standard Edition定位在个人计算机上的应用 J2ME( ...

  3. Foundation ----->NSArray

    .数组的创建     //注意:在OC的数组中,只能够存放对象 //    NSArray *array = [NSArray arrayWithObject:12];错误          //创建 ...

  4. Linux下apache+phppgadmin安装配置

    1.安装pg 安装PostgreSQL数据库 修改pg_hba.conf配置文件,使得数据库可以通过外部访问. 具体可以配置为: # TYPE DATABASE USER ADDRESS METHOD ...

  5. pdo 整套类的封装,保存修改查询

    <?php /** * */ class Db{ private $host = ''; private $port = ''; private $user = ''; private $pas ...

  6. JVM GC原理

    JVM原理 1.分代回收(目前JDK都采用此方式) 采用分治的思想,进行代的划分,把不同生命周期的对象放在不同代上,不同代上采用最适合它的垃圾回收方式进行回收.非堆区有CMS Perm Gen(持久化 ...

  7. 读《JavaScript DOM编程艺术》

    国庆假期花了三四天看了这本书,书很薄,因为是入门书,干货也不是很多. 4. childNodes nodeType nodeValue firstChild lastChild childNodes返 ...

  8. ASP.NET页面回车键触发Button按钮事件问题

    首先了解一下Button.UseSubmitBehavior属性. 获取或设置一个布尔值,该值指示 Button 控件使用客户端浏览器的提交机制还是 ASP.NET 回发机制. 如果该控件使用了客户端 ...

  9. MySQL 第二篇

    一.MySQL多实例介绍 mysql多实例,共用一套mysql安装程序,使用不同的配置文件(my.cnf).启动程序.和数据文件,即在一台服务器上同时开启多个不同的服务器端口(3306,3307),同 ...

  10. 学习制作第一个 openfire 插件

    本文地址:http://www.cnblogs.com/jying/p/3683409.html 蛋疼的自学路~~~ 开始想法是修改openfire源码,但修改后发现不好测试,不会发布,不会使用,各种 ...