在上一篇C#多线程之线程同步篇1中,我们主要学习了执行基本的原子操作、使用Mutex构造以及SemaphoreSlim构造,在这一篇中我们主要学习如何使用AutoResetEvent构造、ManualResetEventSlim构造和CountDownEvent构造。

四、使用AutoResetEvent构造

  在这一小节中,我们将学习如何使用AutoResetEvent构造从一个线程向另一个线程发送通知。AutoResetEvent通知一个等待线程某个事件已经发生。具体步骤如下所示:

1、使用Visual Studio 2015创建一个新的控制台应用程序。

2、双击打开“Program.cs”文件,编写代码如下所示:

 using System;
using System.Threading;
using static System.Console;
using static System.Threading.Thread; namespace Recipe04
{
class Program
{
private static AutoResetEvent workerEvent = new AutoResetEvent(false);
private static AutoResetEvent mainEvent = new AutoResetEvent(false); static void Process(int seconds)
{
WriteLine("Starting a long running work...");
Sleep(TimeSpan.FromSeconds(seconds));
WriteLine("Work is done!");
workerEvent.Set();
WriteLine("Waiting for a main thread to complete its work");
mainEvent.WaitOne();
WriteLine("Starting second operation...");
Sleep(TimeSpan.FromSeconds(seconds));
WriteLine("Work is done!");
workerEvent.Set();
} static void Main(string[] args)
{
var t = new Thread(() => Process());
t.Start(); WriteLine("Waiting for another thread to complete work");
workerEvent.WaitOne();
WriteLine("First operation is completed!");
WriteLine("Performing an operation on a main thread");
Sleep(TimeSpan.FromSeconds());
mainEvent.Set();
WriteLine("Now running the second operation on a second thread");
workerEvent.WaitOne();
WriteLine("Second operation is completed!");
}
}
}

3、运行该控制台应用程序,运行效果(每次运行效果可能不同)如下图所示:

  在第10~11行代码处,我们定义了两个AutoResetEvent实例:workerEvent和mainEvent。workerEvent用于从新建线程中向主线程发送通知,mainEvent用于从主线程向新建线程发送通知。在调用AutoResetEvent的构造方法的时候,我们给该构造方法的“initialState”参数传递了false值,指定AutoResetEvent实例的初始状态为“无信号状态”,这意味着调用AutoResetEvent实例的“WaitOne”方法的线程将会被阻塞,直到我们调用AutoResetEvent实例的“Set”方法之后,该线程才会继续执行。如果我们将AutoResetEvent类的构造方法的“initialState”参数值设置为true,则AutoResetEvent实例的初始状态为“信号状态”,那么第一个调用AutoResetEvent实例的“WaitOne”方法的线程将会被立即执行,然后AutoResetEvent实例的状态自动变为“无信号状态”,这个时候,当我们再次调用AutoResetEvent的“WaitOne”方法后,必须在另一个线程中调用AutoResetEvent的“Set”方法才能继续执行当前的线程。

  在第29行代码处,我们创建了一个新的线程用于执行“Process”方法,并在第30行代码处启动线程。

  在第33行代码处,我们调用AutoResetEvent实例workerEvent的“WaitOne”方法,导致主线程被阻塞,然而在我们在第29行代码处创建的线程中,我们调用了AutoResetEvent实例WorkerEvent的“Set”方法,因此,主线程得以继续执行。当执行到第20行代码处,我们在新建线程中调用了AutoResetEvent实例mainEvent的“WaitOne”方法,因此导致新建线程被阻塞,然而在主线程执行到第37行代码处,我们调用了AutoResetEvent实例mainEvent的“Set”方法,因此,新建线程得以继续执行。而主线程在执行到第39行代码处,主线程又被阻塞,而新建线程执行到第24行代码处,导致主线程得以继续执行,因此,主线程执行到第40行代码,控制台应用程序正常结束。

  AutoResetEvent是kernel-time构造,因此,如果没有必要,我们建议使用下一节介绍的ManualResetEventslim来替代AutoResetEvent。

五、使用ManualResetEventSlim构造

  在这一小节中,我们将学习如何使用ManualResetEventSlim构造在多个线程之间更加灵活地发送通知。具体步骤如下所示:

1、使用Visual Studio 2015创建一个新的控制台应用程序。

2、双击打开“Program.cs”文件,编写代码如下所示:

 using System;
using System.Threading;
using static System.Console;
using static System.Threading.Thread; namespace Recipe05
{
class Program
{
private static ManualResetEventSlim mainEvent = new ManualResetEventSlim(false); static void TravelThroughGates(string threadName, int seconds)
{
WriteLine($"{threadName} falls to sleep");
Sleep(TimeSpan.FromSeconds(seconds));
WriteLine($"{threadName} waits for the gates to open!");
mainEvent.Wait();
WriteLine($"{threadName} enters the gates!");
} static void Main(string[] args)
{
var t1 = new Thread(() => TravelThroughGates("Thread 1", ));
var t2 = new Thread(() => TravelThroughGates("Thread 2", ));
var t3 = new Thread(() => TravelThroughGates("Thread 3", )); t1.Start();
t2.Start();
t3.Start(); Sleep(TimeSpan.FromSeconds());
WriteLine("The gates are now open!");
mainEvent.Set();
Sleep(TimeSpan.FromSeconds());
mainEvent.Reset();
WriteLine("The gates have been closed!");
Sleep(TimeSpan.FromSeconds());
WriteLine("The gates are now open for the second time!");
mainEvent.Set();
Sleep(TimeSpan.FromSeconds());
WriteLine("The gates have been closed!");
mainEvent.Reset();
}
}
}

3、运行该控制台应用程序,运行效果(每次运行效果可能不同)如下图所示:

  在第10行代码处,我们定义了一个ManualResetEventSlim类型的实例mainEvent,并给它的构造方法的“initialState”参数传递了false值,表示该对象的初始状态为“无信号状态”。

  在第23~25行代码处,我们创建了三个线程t1、t2和t3。这三个线程都用于执行“TraveThroughGates”方法,在该方法的内部,我们调用了ManualResetEventSlim实例mainEvent的“Wait”方法,以阻塞t1、t2和t3线程的执行。

  在第31行代码处,我们让主线程阻塞6秒钟,在这六秒钟内,线程t1和t2都执行到第17行代码处,这个时候线程t1和t2都阻塞,并且等待mainEvent的“Set”方法被调用,以接收信号后继续执行。主线程阻塞6秒钟后,会执行第33行代码,执行完毕这行代码之后,线程t1和t2都会接收到通知,因此,线程t1和t2都会继续往下执行,从而都执行第18行代码,之后线程t1和t2执行完毕,结束。

  由于线程t3在主线程执行到第33行代码处的时候,还在阻塞(因为执行了第15行代码)中,因此线程t3在主线程执行到第33行代码处的时候不受影响,继续阻塞。

  当主线程执行到第34行代码处的时候,线程t3依然在阻塞状态中。在主线程执行了第35行代码之后,mainEvent被重置为“无信号状态”。当主线程执行到第37行代码处,主线程被阻塞10秒钟。在主线程被阻塞的10秒钟内,线程t3会执行到第17行代码处,从而t3线程被阻塞,等待通知的到来,才能继续执行。

  当主线程阻塞10秒钟之后,会执行第39行代码,从而导致线程t3继续执行,因此会执行第18行代码,线程t3结束。

  然后主线程阻塞2秒钟后,又将mainEvent重置为“无信号状态”,然后主线程结束。

六、使用CountdownEvent构造

  在这一小节中,我们将学习如何使用CountdownEvent构造等待发送一定数量的通知后,才继续执行被阻塞的线程。学习步骤如下所示:

1、使用Visual Studio 2015创建一个新的控制台应用程序。

2、双击打开“Program.cs”文件,编写代码如下所示:

 using System;
using System.Threading;
using static System.Console;
using static System.Threading.Thread; namespace Recipe06
{
class Program
{
private static CountdownEvent countdown = new CountdownEvent(); static void PerformOperation(string message, int seconds)
{
Sleep(TimeSpan.FromSeconds(seconds));
WriteLine(message);
countdown.Signal();
} static void Main(string[] args)
{
WriteLine("Starting two operations");
var t1 = new Thread(() => PerformOperation("Operation 1 is completed", ));
var t2 = new Thread(() => PerformOperation("Operation 2 is completed", )); t1.Start();
t2.Start();
countdown.Wait();
WriteLine("Both operations have been completed.");
countdown.Dispose();
}
}
}

3、运行该控制台应用程序,运行效果如下图所示:

  在第10行代码处,我们创建了一个CountdownEvent的实例countdown,并给该构造方法的“initialCount”参数传递了数值2,表示我们希望等待2个通知发送完毕后,被阻塞的线程才能继续执行。

  在第22~23行代码处,我们创建了两个新线程用于执行“PerformOperation”方法,在该方法中,我们调用了countdown的“Signal”方法,用于发送通知,并减小CountdownEvent的CurrentCount的值,当CurrentCount的值减少到0时,被阻塞的线程才能继续执行。

  在第27行代码处,我们在主线程中调用了countdown的“Wait”方法,从而主线程被阻塞,直到接收到通知并且CurrentCount的值为0时,主线程才能继续执行。

  注意,如果将第10行代码处的2修改为3,再次运行该程序,主线程会一直等待,不会结束,因为CurrentCount的值没有减少到0。

C#多线程之线程同步篇2的更多相关文章

  1. C#多线程之线程同步篇3

    在上一篇C#多线程之线程同步篇2中,我们主要学习了AutoResetEvent构造.ManualResetEventSlim构造和CountdownEvent构造,在这一篇中,我们将学习Barrier ...

  2. C#多线程之线程同步篇1

    在多线程(线程同步)中,我们将学习多线程中操作共享资源的技术,学习到的知识点如下所示: 执行基本的原子操作 使用Mutex构造 使用SemaphoreSlim构造 使用AutoResetEvent构造 ...

  3. C#多线程之线程池篇1

    在C#多线程之线程池篇中,我们将学习多线程访问共享资源的一些通用的技术,我们将学习到以下知识点: 在线程池中调用委托 在线程池中执行异步操作 线程池和并行度 实现取消选项 使用等待句柄和超时 使用计时 ...

  4. 关于Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇高质量的博文)

    Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文) 前言:在学习多线程时,遇到了一些问题,这里我将这些问题都分享出来,同时也分享了几篇其他博客主的博客,并且将我个人的理解也分享 ...

  5. C#多线程之线程池篇3

    在上一篇C#多线程之线程池篇2中,我们主要学习了线程池和并行度以及如何实现取消选项的相关知识.在这一篇中,我们主要学习如何使用等待句柄和超时.使用计时器和使用BackgroundWorker组件的相关 ...

  6. C#多线程之线程池篇2

    在上一篇C#多线程之线程池篇1中,我们主要学习了如何在线程池中调用委托以及如何在线程池中执行异步操作,在这篇中,我们将学习线程池和并行度.实现取消选项的相关知识. 三.线程池和并行度 在这一小节中,我 ...

  7. 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLock

    [源码下载] 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLoc ...

  8. 重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEvent, AutoResetEvent

    [源码下载] 重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore, CountdownEvent, Barrier, ManualResetEve ...

  9. IOS 多线程,线程同步的三种方式

    本文主要是讲述 IOS 多线程,线程同步的三种方式,更多IOS技术知识,请登陆疯狂软件教育官网. 一般情况下我们使用线程,在多个线程共同访问同一块资源.为保护线程资源的安全和线程访问的正确性. 在IO ...

随机推荐

  1. DB1:数据库的创建和文件的修改

    在SQL Server中,使用Create Database创建数据库,使用Alter Database命令,能够修改数据库的数据文件和日志文件. 一,创建数据库 1,在创建数据库时,最佳实践是: 创 ...

  2. CorelDRAW X8 如何破解激活(附国际版安装包+激活工具) 2016-12-15

    之前有位搞平面的好友“小瘦”说CDR X8无法破解,只能用X7.呃……呃……呃……好像是的 其实CDR8难激活主要在于一个点“没有离线激活了,只可以在线激活”,逆天不是专供逆向的,当然没能力去破解,这 ...

  3. zookeeper源码分析之一服务端启动过程

    zookeeper简介 zookeeper是为分布式应用提供分布式协作服务的开源软件.它提供了一组简单的原子操作,分布式应用可以基于这些原子操作来实现更高层次的同步服务,配置维护,组管理和命名.zoo ...

  4. ASP.NET MVC一次删除多笔记录

    批量删除数据记录,如下面的截屏: 先选中想删除的记录,然后点一下删除铵钮,系统将把选中的记录一次性删除.在此,Insus.NET不想每删除一笔记录连接一次数据库. 因此需要把选择的记录一次上传至服务器 ...

  5. ActionContext.getContext().getSession()

    ActionContext.getContext().getSession() 获取的是session,然后用put存入相应的值,只要在session有效状态下,这个值一直可用 ActionConte ...

  6. BPM体系文件管理解决方案分享

    一.方案概述 企业管理在很大程度上是通过文件化的形式表现出来,体系文件管理是管理体系存在的基础和证据,是规范企业管理活动和全体人员行为,达到管理目标的管理依据.对与公司质量.环境.职业健康安全等体系有 ...

  7. Java 进阶 hello world! - 中级程序员之路

    Java 进阶 hello world! - 中级程序员之路 Java是一种跨平台的语言,号称:"一次编写,到处运行",在世界编程语言排行榜中稳居第二名(TIOBE index). ...

  8. 一个简单的网站web项目的详解

    有不对的术语,或者不好理解的部分,欢迎大家批评指正,谢谢大家! 近期做的网站web项目,实现登录功能,查询功能.首先把这个项目分为几个模块来处理,当前用户模块,历史用户模块,历史记录模块,数据库模块, ...

  9. AngularJS 系列 学习笔记 目录篇

    目录: AngularJS 系列 01 - HelloWorld和数据绑定 AngularJS 系列 02 - 模块 (持续更新)

  10. 读过MBA的CEO更自私?《哈佛商业评论》2016年第12期。4星

    老牌管理杂志.每期都值得精度.本期我还是给4星. 以下是本书中的一些内容的摘抄: 1:他们发现在Airbnb上,如果客人姓名听起来像黑人,那么比名字像白人的客人的接受率会低16%.#45 2:对立组织 ...