前几天一朋友问我如何实现线程的顺序执行,说真的,虽然看过CLR这本书,也把线程部分拜读了两遍,但是这个问题出来之后还是没有一个思路。今天在搜索资料的时候无意中再次看到AutoResetEvent这个东西,当然我知道它是和线程有关,用于处理线程切换之类的(可能在测试Demo之前理解有误),于是决定用AutoResetEvent来处理上面的问题。

这里以园区一个园友的例子来说明,这个例子就是 买书--》付款--》拿书这个过程,该过程会持续n(通过变量设置)次,并且每一次都要按照顺序执行,有可能有同学会疑问,直接Sleep不就好了,干嘛非要多个线程,如果处理某一个环节的时间过久或者是业务复杂,那么整个程序就直接未响应了,所以这里加入多线程来保证程序的响应。

class Program
{
//循环次数
const int numIterations = 10;
//买书
static AutoResetEvent buyResetEvent = new AutoResetEvent(false);
//付款
static AutoResetEvent payResetEvent = new AutoResetEvent(false);
//取书
static AutoResetEvent getBookEvent = new AutoResetEvent(false);
//循环的次数
static int number;
static void Main(string[] args)
{
//付款线程
Thread payMoneyThread = new Thread(new ThreadStart(PayMoneyProc));
payMoneyThread.Name = "付钱线程"; //取书线程
Thread getBookThread = new Thread(new ThreadStart(GetBookProc));
getBookThread.Name = "取书线程"; payMoneyThread.Start();
getBookThread.Start(); for (int i = 1; i <= numIterations; i++)
{
Console.WriteLine("买书线程:数量{0}", i);
number = i;
//允许付款线程等待
payResetEvent.Set();
//禁止买书线程等待
buyResetEvent.WaitOne();
}
Console.Read();
} static void PayMoneyProc()
{
while (true)
{
//等待付款
payResetEvent.WaitOne();
Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number);
//允许取书线程等待
getBookEvent.Set();
}
}
static void GetBookProc()
{
while (true)
{
//等待付款
getBookEvent.WaitOne();
Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number);
Console.WriteLine("------------------------------------------");
//允许买书线程等待(到这一步一个完整的买书流程就执行结束了,完全按照买书--》付款--》取书的流程)
buyResetEvent.Set();
}
}
}

上述代码不复杂,但是有几个关键的对象和方法,接下来详细进行说明。

1.分别定义了买书、付款、拿书三个AutoResetEvent,注意定义的时候传入了false,这也AutoSet默认是不可用的,需要手动调用Set方法才可以呢。该对象是决定是否能WaitOne一个请求的关键,当AutoResetEvent.Set执行则可以使用AutoResetEvent.WaitOne进行一个等待请求,如果再有WaitOne请求,则要继续等待Set的执行;

2.先看for循环,在循环中我们使用payResetEvent.Set()这句代码,这也PayThread中的WaitOne请求就可以获得批准(下文有介绍),同时我们又加上了buyResetEvent.WaitOne()(这样在上一次购买流程结束之前,新的一次流程是不可以执行的,这也就是我们的最大问题,保证线程按照顺序执行);

3.定义了付款和拿书的Thread,并且在方法内都是While循环,该循环主要是为了可以将我们的过程进行多次,毕竟线程只会执行一遍嘛。当然这个不是重点,重点是While中我们的操作,先看PayThrea的操作,先调用payResetEvent.WaitOne()请求一个等待操作,当然可以立马执行,因为在 2 中我们说了for中是调用了payResetEvent.Set()操作,这样就可以直接得到一个请求响应,输出关键信息,然后又到了重点,我们调用了getBookEvent.Set(),这是为什么呢,因为付款不成功是不可以拿书走的啊,那样就是偷盗了。注意、注意、注意,重要的话说三遍,GetBookThread操作中也有一个waitOne请求,可是为什么不会执行呢,因为没有Set呢,我们初始化AutoResetEvent的时候我们设置的是false,这样默认就不可以得到一个请求,必须手动调用Set才可以,由于在PayMoneyThread的最后一行代码中我们调用了getBookEvent.Set(),这样getBookEvent.WaitOne就可用了。在GetBookThread中依次输出关键信息,最后一行代码又来了,再次调用了一个AutoResetEvent.Set(),没错就是买书的对象,因为到了这一步完整的买书流程就结束了,可以再次买书了啊啊啊啊啊啊,可以买书了,好开心啊。然后再次回到for循环中的最后一行代码,buyResetEvent.Wait()此时就复活了,开始第好几次的买书流程。

运行截图如下(绝对真实,毫无PS):

好了,退朝,有事改天上朝再议。

Update:鉴于大家对这个使用方式有不同的建议,另外.net的版本都4.6了,所以重新使用Task的方式进行了更新,欢迎继续拍砖.

 static void Main(string[] args)
{
for (int i = ; i < ; i++)
{
Task buyTask = Task.Factory.StartNew(() =>
{
BuyBook();
}).ContinueWith((state) =>
{
PayMoney();
}).ContinueWith((state) =>
{
TakeBook();
});
Task.WaitAll(buyTask);
Console.WriteLine();
} Console.Read();
} private static void BuyBook()
{
Console.WriteLine("书太多了,挑花眼了");
} private static void PayMoney()
{
Console.WriteLine("先付钱才能取书哦");
} private static void TakeBook()
{
Console.WriteLine("取书喽");
}

C#之使用AutoResetEvent实现线程的顺序执行的更多相关文章

  1. Qt 控制线程的顺序执行(使用QWaitCondition,并且线程类的run函数里记得加exec(),使得线程常驻)

    背景项目中用到多线程,对线程的执行顺序有要求: A.一个线程先收数据 B.一个线程处理数据 C.一个线程再将处理后的数据发送出去 要求三个线程按照ABC的顺序循环执行. 思路子类化多线程方法 重写子类 ...

  2. 【Java并发】线程的顺序执行

    /** * 问题:有线程a.b.c,如何让它们顺序执行? * 方式一:可用Join()方法实现 * 方式二:可用newSingleThreadExecutor() * Created by Smile ...

  3. 三个线程abc顺序执行

    1.使用synchronized悲观锁(秋招阿里的一个笔试题,应该写的比较复杂,然后就没有然后了o(╥﹏╥)o) public class ThreadThreadp { private int fl ...

  4. java 多线程 实现多个线程的顺序执行

    场景 编写一个程序,启动三个线程,三个线程的name分别是A,B,C:,每个线程将自己的ID值在屏幕上打印5遍,打印顺序是ABCABC... 使用 synchronized 实现 public cla ...

  5. 用三个线程按顺序循环打印ABC三个字母

    有两种方法:semaphore信号量和mutex互斥锁.需要注意的是C++11已经没有semaphore. C++ 并发编程(六):信号量(Semaphore) - 止于至善 - SegmentFau ...

  6. Java中如何保证线程顺序执行

    只要了解过多线程,我们就知道线程开始的顺序跟执行的顺序是不一样的.如果只是创建三个线程然后执行,最后的执行顺序是不可预期的.这是因为在创建完线程之后,线程执行的开始时间取决于CPU何时分配时间片,线程 ...

  7. [Thread] 多线程顺序执行

    Join 主线程join 启动线程t1,随后调用join,main线程需要等t1线程执行完毕后继续执行. public class MainJoin { static class MyThread i ...

  8. AutoResetEvent信号锁 waitone set 执行一次线程退出 挺不爽的地方

    下边有个 循环调用线程写奇偶数的程序 class TheadTest { //定义一个Stream对象接收打开文件 private FileStream st; //构造方法 public Thead ...

  9. C# 使用AutoResetEvent进行线程同步

    AutoResetEvent 允许线程通过发信号互相通信. 通常,当线程需要独占访问资源时使用该类. 线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号. 如果 AutoRe ...

随机推荐

  1. cocos2dx中导演的职责有哪些?

    1.一个游戏里面只有一个导演,因此采用了单例的设计模式,用getInstance方法来获得 2.游戏中导演负责openGL ES的初始化,场景的切换,游戏的暂停继续(相当于拍电影的ka),节点坐标与世 ...

  2. PHP URL 重定向 的三种方法(转载)

    为了方便查询,转载一篇. 1.使用header()函数    PHP的HTTP相关函数种提供了一个 header()函数,首先要清楚,header()函数必须放在php程序的开头部分,而且之前不能有另 ...

  3. python中fork()函数生成子进程分析

    python的os module中有fork()函数用于生成子进程,生成的子进程是父进程的镜像,但是它们有各自的地址空间,子进程复制一份父进程内存给自己,两个进程之 间的执行是相互独立的,其执行顺序可 ...

  4. ImportError: No module named _winreg

    在Flask项目部署到linux上时出现了一个问题,但是在windows上是好的,没有问题.以下是bug Traceback (most recent call last): File "f ...

  5. Node.js 学习(二) 创建第一个应用

    如果我们使用PHP来编写后端的代码时,需要Apache 或者 Nginx 的HTTP 服务器,并配上 mod_php5 模块和php-cgi. 从这个角度看,整个"接收 HTTP 请求并提供 ...

  6. 委托、匿名委托和lambda表达式

    1.委托 在.NET中,委托有点类似于C/C++中的函数指针,但与指针不同的是,委托是一种安全的类型,那么我们就以实现两个数的差为例,先声明一个成员方法: public int CompareTwoV ...

  7. MySQL基本概念

    MySQL 数据库 MySQL可分为三个层次:文件层次,服务层次,界面 常用的数据类型:int 整数float double decimal ——小数(要在十进位栏里设置小数点后最大显示位数)varc ...

  8. hdu 1301 Jungle Roads 最小生成树

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1301 The Head Elder of the tropical island of Lagrish ...

  9. 【bzoj1008】[HNOI2008]越狱

    1008: [HNOI2008]越狱 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 7692  Solved: 3296[Submit][Status] ...

  10. √新技能Get - 教你发空白朋友圈

    今天下午都被空白朋友圈刷屏了.空白朋友圈也即是在朋友圈里面发空消息,没有图片也没有文字,朋友圈动态是空空的.这是谁在恶搞呢?怎么实现呢? 怎么发空消息啊?其实这是为了帮助大家识别身边用iOS的小伙伴的 ...