说明

之前在博客园看到有位仁兄发表一篇关于AutoResetEvent介绍,看了下他写的代码,看上去没什么问题,但仔细看还是能发现问题。下图是这位仁兄代码截图。

仁兄博客地址:http://www.cnblogs.com/lzjsky/archive/2011/07/11/2102794.html

按照这种写法自己试了下,运行起来并不是他这种结果(运行结果很随机)。

原因有以下两点:

1、支付线程与取书线程都属于同级线程,运行先后顺序是随机的

2、在循环内部调用AutoResetEvent.Set(),不能确定子线程是否按顺序执行,有可能主线程已经循环多次,而子线程可能才循环一次

修正

首先,要明白实验的场景。还是引用这位仁兄的例子:“我去书店买书,当我选中一本书后我会去收费处付钱,付好钱后再去仓库取书。这个顺序不能颠倒,我作为主线程,收费处和仓库做两个辅助线程” 。

要实现上图这种效果,得先确定好执行先后顺序(上面已经说过):挑书-->收费-->取书-->完成

代码编写如下:

 class Program
{
static int _num = ;
//本例重点对象
static AutoResetEvent _autoReset = new AutoResetEvent(false); static AutoResetEvent _autoReset0 = new AutoResetEvent(false);
static AutoResetEvent _autoReset1 = new AutoResetEvent(false); //static AutoResetEvent autoReset2 = new AutoResetEvent(false);
//static AutoResetEvent autoReset3 = new AutoResetEvent(false); //static object _payMoneyObj = new object();
//static object _getBookObj = new object(); private static void ThreadPayMoneyProc()
{
while (true)
{
//_autoReset.WaitOne();
_autoReset0.WaitOne();
//lock (_payMoneyObj)
{
Console.WriteLine(Thread.CurrentThread.Name + ",编号: " + _num);
//通知主线程,钱已付完
//_autoReset2.Set();
}
}
} private static void TreadGetBookProc()
{
while (true)
{
//_autoReset.WaitOne();
_autoReset1.WaitOne();
//lock (_getBookObj)
{
Console.WriteLine(Thread.CurrentThread.Name + ",编号: " + _num);
//通知主线程,书已取走
//_autoReset3.Set();
}
}
} static void Main(string[] args)
{
//本案例是通过AutoResetEvent来实现多线程同步
//购买书数量
const int num = ; //付钱线程
Thread threadPayMoney = new Thread(new ThreadStart(ThreadPayMoneyProc));
threadPayMoney.Name = "付钱线程";
//取书线程
Thread threadGetBook = new Thread(new ThreadStart(TreadGetBookProc));
threadGetBook.Name = "取书线程"; //开始执行线程
threadPayMoney.Start();
threadGetBook.Start(); //主线程开始选书
Console.WriteLine("----------------主线程开始选书!------------------");
for (int i = ; i <= num; i++)
{
Console.WriteLine("主线程选书编号:" + i);
_num = i;
//_autoReset.Set(); //通知付钱线程
_autoReset0.Set();
//主线延时1ms执行(但不知道付钱线程这个过程需要多少时间)
Thread.Sleep();
//_autoReset2.WaitOne(); //付完钱后,通知取书线程
_autoReset1.Set();
//主线延时1ms执行(但不知道取书线程这个过程需要多少时间)
Thread.Sleep();
//_autoReset3.WaitOne();
Console.WriteLine("-----------------------------------");
} Console.ReadKey(); }
}

运行结果如下图:

这样做,效果是出来了,但主线程不知道付费线程、取书线程执行需要多长时间。上例中给定的是1ms,但如果其中某个子线程超过了给定的休眠时间,主线会继续往下执行,不会等待子线程处理完成。这样就导致了买书编号与付钱和取书的编号不同步。也就混乱了。

这时可以使用AutoResetEvent这个对象。上例中已经使用这个对象。没错,还可以在继续使用。

代码如下图:

 class Program
{
static int _num = ;
//本例重点对象
static AutoResetEvent _autoReset = new AutoResetEvent(false); static AutoResetEvent _autoReset0 = new AutoResetEvent(false);
static AutoResetEvent _autoReset1 = new AutoResetEvent(false); static AutoResetEvent _autoReset2 = new AutoResetEvent(false);
static AutoResetEvent _autoReset3 = new AutoResetEvent(false); //static object _payMoneyObj = new object();
//static object _getBookObj = new object(); private static void ThreadPayMoneyProc()
{
while (true)
{
//_autoReset.WaitOne();
_autoReset0.WaitOne();
//lock (_payMoneyObj)
{
Console.WriteLine(Thread.CurrentThread.Name + ",编号: " + _num);
//通知主线程,钱已付完成
_autoReset2.Set();
}
}
} private static void TreadGetBookProc()
{
while (true)
{
//_autoReset.WaitOne();
_autoReset1.WaitOne();
//lock (_getBookObj)
{
Console.WriteLine(Thread.CurrentThread.Name + ",编号: " + _num);
//通知主线程,书已取走
_autoReset3.Set();
}
}
} static void Main(string[] args)
{
//本案例是通过AutoResetEvent来实现多线程同步
//购买书数量
const int num = ; //付钱线程
Thread threadPayMoney = new Thread(new ThreadStart(ThreadPayMoneyProc));
threadPayMoney.Name = "付钱线程";
//取书线程
Thread threadGetBook = new Thread(new ThreadStart(TreadGetBookProc));
threadGetBook.Name = "取书线程"; //开始执行线程
threadPayMoney.Start();
threadGetBook.Start(); //主线程开始选书
Console.WriteLine("----------------主线程开始选书!------------------");
for (int i = ; i <= num; i++)
{
Console.WriteLine("主线程选书编号:" + i);
_num = i;
//_autoReset.Set(); //通知付钱线程
_autoReset0.Set();
//主线延时1ms执行(但不知道付钱线程这个过程需要多少时间)
//Thread.Sleep(1);
//等待付钱线程
_autoReset2.WaitOne(); //付完钱后,通知取书线程
_autoReset1.Set();
//主线延时1ms执行(但不知道取书线程这个过程需要多少时间)
//Thread.Sleep(1);
//等待取书线程
_autoReset3.WaitOne();
Console.WriteLine("-----------------------------------");
//完成后,继续下一个任务处理
} Console.ReadKey(); }
}

运行结果如下图:

运行结果和上面使用指定主线程休眠所运行结果是一样的。但是,可以不用指定主线程休眠时间,也不需要指定。因为你没法估计子线程所运行的时间,而且每次运行时间都不一样。

后话

本例中, 买书场景其实有两种编程结构(或者编程思想)。一种是本例中的,买书是主线程,而收银台(付钱线程)、仓库(取书线程)。这两个线程是一直存在的,一直跑着的。只要有书过来,这两个线程就会执行。这可以联系到现实中的收银台和仓库。

第二种编程思想,买书是一个发起线程,然后开启一个付款线程和取书线程。这时,买书线程(主线程)可以确定这两个子线程什么时候执行完成。使用 线程对象.Join(),执行完后,主线程接着下步任务处理。

以上是自己对“C#关于AutoResetEvent的使用介绍”文章的修改以及对AutoResetEvent的使用总结。不足之处请各位指点一二。

C#关于AutoResetEvent的使用介绍----修正的更多相关文章

  1. Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群

    Redis总结(五)缓存雪崩和缓存穿透等问题   前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhon ...

  2. C#关于AutoResetEvent的使用介绍(用AutoResetEvent实现同步)

    前几天碰到一个线程的顺序执行的问题,就是一个异步线程往A接口发送一个数据请求.另外一个异步线程往B接口发送一个数据请求,当A和B都执行成功了,再往C接口发送一个请求.说真的,一直做BS项目,对线程了解 ...

  3. C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步)

    前几天碰到一个线程的顺序执行的问题,就是一个异步线程往A接口发送一个数据请求.另外一个异步线程往B接口发送一个数据请求,当A和B都执行成功了,再往C接口发送一个请求.说真的,一直做BS项目,对线程了解 ...

  4. AutoResetEvent的使用介绍(用AutoResetEvent实现同步)

    前几天碰到一个线程的顺序执行的问题,就是一个异步线程往A接口发送一个数据请求.另外一个异步线程往B接口发送一个数据请求,当A和B都执行成功了,再往C接口发送一个请求.说真的,一直做BS项目,对线程了解 ...

  5. WaitHandle、AutoResetEvent、ManualResetEvent

    多线程中的锁系统(三)-WaitHandle.AutoResetEvent.ManualResetEvent 介绍 本章主要说下基于内核模式构造的线程同步方式,事件,信号量. 目录 一:理论 二:Wa ...

  6. C#总结(二)事件Event 介绍总结

    最近在总结一些基础的东西,主要是学起来很难懂,但是在日常又有可能会经常用到的东西.前面介绍了 C# 的 AutoResetEvent的使用介绍, 这次介绍事件(event). 事件(event),对于 ...

  7. 事件Event 介绍总结

    最近在总结一些基础的东西,主要是学起来很难懂,但是在日常又有可能会经常用到的东西.前面介绍了 C# 的 AutoResetEvent的使用介绍, 这次介绍事件(event). 事件(event),对于 ...

  8. EditPlus(4.0.0.395)中文免激活绿色版

    EditPlus一套功能强大,可取代记事本的文字编辑器,拥有无限制的撤消与重做.英文拼字检查.自动换行.列数标记.搜寻取代.同时编辑多文件.全屏幕浏览功能.而它还有一个好用的功能,就是它有监视剪贴板的 ...

  9. 关于 AutoResetEvent 的介绍的简单示例

    关于 AutoResetEvent 的介绍的简单示例 直接贴代码了: class Program { static void Main(string[] args) { string result = ...

随机推荐

  1. Struts + Spring + Hibernate 进阶开端(一)

    Long Long ago,就听说过SSH,起初还以为是一个东东,具体内容更是不详,总觉得高端大气上档次,经过学习之后才发现,不仅仅是高大上,更是低调奢华有内涵,经过一段时间的研究和学习SSH框架的基 ...

  2. Grow heap (frag case) to 6.437MB for 1114126-byte allocation

    本篇文章是对Grow heap (frag case) 堆内存过大的问题进行了详细的分析介绍,需要的朋友参考下 对于Android开发者来说虽然使用了可以自动管理内存的Java语言,但是对于内存管理不 ...

  3. DLL内存分配与共享

    一旦DLL的文件映像被映射到调用进程的地址空间中,DLL的函数就可以供进程中运行的所有线程使用.实际上,DLL几乎将失去它作为DLL的全部特征.对于进程中的线程来说,DLL的代码和数据看上去就像恰巧是 ...

  4. IOS安装CocoaPods完整流程

    作为一个底层系统大菜鸟,又搞过几年ios来说,安装一个CocoaPods是一件蛋痛的事~  说懂又懂,说不懂又不懂.    由于安装过程比較复杂,步骤较多,而网上教程又比較零散,并且有一些是扯蛋的,所 ...

  5. eclipse git拉取完代码后怎么maven构建

    这个参考下:https://jingyan.baidu.com/article/414eccf64d29bc6b431f0ade.html 没试过上面的,反正git后的文件夹非空,用maven创建会失 ...

  6. 腾讯云 net.core

    搭建 .NET Core 开发环境 安装 .Net Core 执行代码 任务时间:时间未知 .NET Core 的官方文档很详细,本实验带你建立一个.NET Core 1.1的Web运行环境,更多内容 ...

  7. atitit.TokenService  token服务模块的设计

    atitit.TokenService  token服务模块的设计 1. Token的归类1 2. Token的用途2 2.1. 访问控制2 2.2. 编译原理术语)编辑2 2.3. 数据处理2 1. ...

  8. DM36x IPNC OSD显示中文 --- 实战篇

    通过数据准备篇,将数据准备好后,其实剩下的工作已经很简单了,通过以下几个步骤即可把一个中文显示在OSD画面上:1. 使用SWOSD_setBmpchangeWinXYPrm函数设置好OSD显示坐标位置 ...

  9. dos指令 批处理文件

    windows下开发的时候难免写一些脚本,脚本的调用又难以避免的写批处理文件,也就是(.bat)文件!这个文件是什么呢?其实就是以下的这些dos命令.以下是从网上摘抄的,留以记录,待以后需要时查阅.也 ...

  10. UVA 11885 - Number of Battlefields(斐波那契)

    11885 - Number of Battlefields 题意:给周长.求能围成的战场数目.不包含矩形. 思路:详细的递推没递推出来,可是看了网上一个规律,假设包含矩形的答案应该是斐波那契数列(可 ...