• C#之使用AutoResetEvent实现线程的顺序执行


    前几天一朋友问我如何实现线程的顺序执行,说真的,虽然看过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 = 0; i < 10; 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("取书喽");
            }
  • 相关阅读:
    JQuery 日历控件
    恢复xp_cmdshell SQL Server阻止了对组件 'xp_cmdshell' 的过程'sys.xp_cmdshell' 启用
    XML基础总结
    09.09.16总结
    草根
    设计模式学习总结
    检索 COM 类工厂中 CLSID 为{0002450000000000C000000000000046} 的组件时失败,原因是出现以下错误: 80070005。
    09.09.22总结
    Singleton 单态模式
    【Tomcat源码学习】5.请求处理
  • 原文地址:https://www.cnblogs.com/ListenFly/p/4788410.html
Copyright © 2020-2023  润新知