说明

之前在博客园看到有位仁兄发表一篇关于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的使用总结。不足之处请各位指点一二。

最新文章

  1. 电信行业的BI应用
  2. Python小练习一
  3. 嵌入式Linux驱动学习之路(八)创建最小的根文件系统
  4. MySQL- 锁(2)
  5. MySQL中删除重复数据的简单方法,mysql删除重复数据
  6. Python 字符串、元组、字典转换成列表
  7. MySQL数据库高并发优化配置
  8. 创建UIImage的两种方法
  9. 【转】 iOS开发 剖析网易新闻标签栏视图切换(addChildViewController属性介绍)
  10. softlayer
  11. 2、Lucene 最简单的使用(小例子)
  12. myeclipse2015复制项目需要修改的地方
  13. docker容器自动退出的问题
  14. python入门(14)定义函数和接收返回值
  15. Ajax实现注册无刷新验证用户名是否存在
  16. Spring Cloud实践:降级、限流、滚动、灰度、AB、金丝雀的实现思路
  17. linux tomcat单机部署多应用
  18. loadrunner中使用web_custom_request函数调用webservice接口
  19. Gitlab8.5安装后邮件发送不成功的解决过程
  20. 从零搭建ES搜索服务(一)基本概念及环境搭建

热门文章

  1. .Net 下未捕获异常的处理
  2. 网页视频播放方案chimee 组件使用
  3. iOS 之UIBezierPath
  4. ASP.NET_SessionId 不相同导致验证码出错
  5. ssl中间证书
  6. silverlight RadGridView总结系列(转载)
  7. jQuery中事件绑定
  8. 论C++STL源代码中关于堆算法的那些事
  9. Xilinx-7Series-FPGA高速收发器使用学习—概述与参考时钟篇
  10. iOS开发多线程篇 11 —自定义NSOperation