• C#关于AutoResetEvent的使用介绍----修正


    说明

    之前在博客园看到有位仁兄发表一篇关于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(),执行完后,主线程接着下步任务处理。

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

  • 相关阅读:
    canvas背景粒子动态变化动画
    点击屏幕弹出心形效果
    前端图片的性能优化
    vue的computed和method的区别
    es6的...
    命名路由和命名视图
    编程式路由
    [思维]蚂蚁感冒
    [模板]前缀树 / 字典树及应用
    [模板]三分搜索
  • 原文地址:https://www.cnblogs.com/backbone/p/4994327.html
Copyright © 2020-2023  润新知