南京软件定制开发

南京倾心软件欢迎您访问本站

13605185909

新闻资讯

NEWS CENTER
栏目导航

南京软件开发之AutoResetEvent使用介绍

发布时间:Mar 23, 2021         已有 人浏览

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

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

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

原因有以下两点:

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

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

修正

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

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

代码编写如下:

复制代码
 1 class Program
 2     {
 3         static int _num = 0;
 4         //本例重点对象
 5         static AutoResetEvent _autoReset = new AutoResetEvent(false);
 6 
 7         static AutoResetEvent _autoReset0 = new AutoResetEvent(false);
 8         static AutoResetEvent _autoReset1 = new AutoResetEvent(false);
 9 
10         //static AutoResetEvent autoReset2 = new AutoResetEvent(false);
11         //static AutoResetEvent autoReset3 = new AutoResetEvent(false);
12 
13         //static object _payMoneyObj = new object();
14         //static object _getBookObj = new object();
15 
16         private static void ThreadPayMoneyProc()
17         {
18             while (true)
19             {
20                 //_autoReset.WaitOne();
21                 _autoReset0.WaitOne();
22                 //lock (_payMoneyObj)
23                 {
24                     Console.WriteLine(Thread.CurrentThread.Name + ",编号: " + _num);
25                     //通知主线程,钱已付完
26                     //_autoReset2.Set();
27                 }
28             }
29         }
30 
31         private static void TreadGetBookProc()
32         {
33             while (true)
34             {
35                 //_autoReset.WaitOne();
36                 _autoReset1.WaitOne();
37                 //lock (_getBookObj)
38                 {
39                     Console.WriteLine(Thread.CurrentThread.Name + ",编号: " + _num);
40                     //通知主线程,书已取走
41                     //_autoReset3.Set();
42                 }
43             }
44         }
45 
46 
47         static void Main(string[] args)
48         {
49             //本案例是通过AutoResetEvent来实现多线程同步
50             //购买书数量
51             const int num = 50;
52 
53             //付钱线程
54             Thread threadPayMoney = new Thread(new ThreadStart(ThreadPayMoneyProc));
55             threadPayMoney.Name = "付钱线程";
56             //取书线程
57             Thread threadGetBook = new Thread(new ThreadStart(TreadGetBookProc));
58             threadGetBook.Name = "取书线程";
59 
60             //开始执行线程
61             threadPayMoney.Start();
62             threadGetBook.Start();
63 
64             //主线程开始选书
65             Console.WriteLine("----------------主线程开始选书!------------------");
66             for (int i = 1; i <= num; i++)
67             {
68                 Console.WriteLine("主线程选书编号:" + i);
69                 _num = i;
70                 //_autoReset.Set();
71 
72                 //通知付钱线程
73                 _autoReset0.Set();
74                 //主线延时1ms执行(但不知道付钱线程这个过程需要多少时间)
75                 Thread.Sleep(1);
76                 //_autoReset2.WaitOne();
77 
78                 //付完钱后,通知取书线程
79                 _autoReset1.Set();
80                 //主线延时1ms执行(但不知道取书线程这个过程需要多少时间)
81                 Thread.Sleep(1);
82                 //_autoReset3.WaitOne();
83                 Console.WriteLine("-----------------------------------");
84             }
85 
86             Console.ReadKey();
87 
88 
89         }
90     }
复制代码

运行结果如下图:

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

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

代码如下图:

复制代码
 1 class Program
 2     {
 3         static int _num = 0;
 4         //本例重点对象
 5         static AutoResetEvent _autoReset = new AutoResetEvent(false);
 6 
 7         static AutoResetEvent _autoReset0 = new AutoResetEvent(false);
 8         static AutoResetEvent _autoReset1 = new AutoResetEvent(false);
 9 
10         static AutoResetEvent _autoReset2 = new AutoResetEvent(false);
11         static AutoResetEvent _autoReset3 = new AutoResetEvent(false);
12 
13         //static object _payMoneyObj = new object();
14         //static object _getBookObj = new object();
15 
16         private static void ThreadPayMoneyProc()
17         {
18             while (true)
19             {
20                 //_autoReset.WaitOne();
21                 _autoReset0.WaitOne();
22                 //lock (_payMoneyObj)
23                 {
24                     Console.WriteLine(Thread.CurrentThread.Name + ",编号: " + _num);
25                     //通知主线程,钱已付完成
26                     _autoReset2.Set();
27                 }
28             }
29         }
30 
31         private static void TreadGetBookProc()
32         {
33             while (true)
34             {
35                 //_autoReset.WaitOne();
36                 _autoReset1.WaitOne();
37                 //lock (_getBookObj)
38                 {
39                     Console.WriteLine(Thread.CurrentThread.Name + ",编号: " + _num);
40                     //通知主线程,书已取走
41                     _autoReset3.Set();
42                 }
43             }
44         }
45 
46 
47         static void Main(string[] args)
48         {
49             //本案例是通过AutoResetEvent来实现多线程同步
50             //购买书数量
51             const int num = 5;
52 
53             //付钱线程
54             Thread threadPayMoney = new Thread(new ThreadStart(ThreadPayMoneyProc));
55             threadPayMoney.Name = "付钱线程";
56             //取书线程
57             Thread threadGetBook = new Thread(new ThreadStart(TreadGetBookProc));
58             threadGetBook.Name = "取书线程";
59 
60             //开始执行线程
61             threadPayMoney.Start();
62             threadGetBook.Start();
63 
64             //主线程开始选书
65             Console.WriteLine("----------------主线程开始选书!------------------");
66             for (int i = 1; i <= num; i++)
67             {
68                 Console.WriteLine("主线程选书编号:" + i);
69                 _num = i;
70                 //_autoReset.Set();
71 
72                 //通知付钱线程
73                 _autoReset0.Set();
74                 //主线延时1ms执行(但不知道付钱线程这个过程需要多少时间)
75                 //Thread.Sleep(1);
76                 //等待付钱线程
77                 _autoReset2.WaitOne();
78 
79                 //付完钱后,通知取书线程
80                 _autoReset1.Set();
81                 //主线延时1ms执行(但不知道取书线程这个过程需要多少时间)
82                 //Thread.Sleep(1);
83                 //等待取书线程
84                 _autoReset3.WaitOne();
85                 Console.WriteLine("-----------------------------------");
86                 //完成后,继续下一个任务处理
87             }
88 
89             Console.ReadKey();
90 
91 
92         }
93     }
复制代码

运行结果如下图:

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

后话

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

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

Copyright © 2020-2022 南京倾心软件技术有限公司 版权所有     苏ICP备2020070309号-1
QQ在线咨询
13605185909
返回顶部