多线程系列1:经典卖票

Posted yusina

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程系列1:经典卖票相关的知识,希望对你有一定的参考价值。

1.卖票的方法

  class TicketRest
    {
        int ticket = 1;
        int Max = 0;
        public TicketRest(int max)
        {
            Max = max;
        }
        /// <summary>
        /// 未加锁
        /// </summary>
        /// <param name="num"></param>
        public void SellTicketNoLock(int num)
        {
            while (ticket <= Max)
            {
                if (ticket > Max)
                    break;//这里一定要判断。
                Console.WriteLine($"窗口{num}|售票员{Thread.CurrentThread.ManagedThreadId}:售出第[{ticket}]号车票,剩余{Max-ticket}张");
               ticket++;
               Task.Delay(200);
            }
        }
        /// <summary>
        ///加锁
        /// </summary>
        /// <param name="num"></param>
        public void SellTicketAddLock(int num)
        {
            while (ticket <= Max)
            {
                lock (this)
                {
                    if (ticket > Max)
                        break;//这里一定要判断。
                    Console.WriteLine($"窗口{num}|售票员{Thread.CurrentThread.ManagedThreadId}:售出第[{ticket}]号车票,剩余{Max - ticket}张");
                    ticket++;
                    Task.Delay(200);
                }
            }
        }
View Code

 

2.使用Thred的方式调用卖票方法

未加锁的情况 (调用方法 SellTicketNoLock)会出现一张票多次卖出的情况和余票不符合的情况。

 

3.使用Task的方式调用卖票方法

闭包的陷阱 :

 

for (int i = 0; i < windowCount; i++)
{
 tasklst.Add(Task.Run(() => { ticketRest2.SellTicketAddLock(i); }));

最后的i值都是5  而不是原来 的i值啦。

 

解决方法 使用临时变量,改为:

for (int i = 0; i < windowCount; i++)
{
 int temp = i;
 tasklst.Add(Task.Run(() => { ticketRest2.SellTicketAddLock(temp); }));
}

 

加锁后(SellTicketAddLock) 车票没有重复 余票也没有出错。

完整的代码:

 

 class Program
    {
        static void Main(string[] args)
        {
            //SellThread();
            SellTaskDemo();
            Console.ReadLine();
        }

        private static int ticketCount = 100;// 共20张票  
        private static int windowCount = 5;// 共5个窗口同时售票  
        /// <summary>
        ///  //演示多线程卖票案例-Thread方式
        /// </summary>
        static void SellThread()
        {
            try
            {
                TicketRest ticketRest = new TicketRest(ticketCount);
                for (int i = 0; i < windowCount; i++)
                {
                    Thread thread = new Thread(() => { ticketRest.SellTicketNoLock(i); });
                    thread.Start();
                }
            }
            catch (Exception ex)
            {
                LoggerFactory.Instance.Logger_Error(ex);
                Console.WriteLine($"{ex.Message}|{ex.GetType().FullName}|{ex.TargetSite}|{ex.StackTrace}");
            }
        }

        /// <summary>
        /// 演示多线程卖票案例-Task方式
        /// </summary>
        static void SellTaskDemo()
        {
            try
            {
                Stopwatch stopwatch1 = new Stopwatch();
                stopwatch1.Start();
                TicketRest ticketRest1 = new TicketRest(ticketCount);
                ticketRest1.SellTicketAddLock(0);
                stopwatch1.Stop();
                Console.WriteLine($"使用单线程方式消耗时间:{stopwatch1.ElapsedMilliseconds}毫秒");

                Stopwatch stopwatch2 = new Stopwatch();
                stopwatch2.Start();
                TicketRest ticketRest2 = new TicketRest(ticketCount);
                var tasklst = new List<Task>();
                for (int i = 0; i < windowCount; i++)
                {
                    int temp = i;
                    tasklst.Add(Task.Run(() => { ticketRest2.SellTicketAddLock(temp); }));
                }
                Task.WaitAll(tasklst.ToArray());
                stopwatch2.Stop();
                Console.WriteLine($"使用多线程方式:代码段运行时间({stopwatch2.ElapsedMilliseconds}毫秒)");
            }
            catch (Exception ex)
            {
                LoggerFactory.Instance.Logger_Error(ex);
                Console.WriteLine($"{ex.Message}|{ex.GetType().FullName}|{ex.TargetSite}|{ex.StackTrace}");
            }
        }
    }

    class TicketRest
    {
        int ticket = 1;
        int Max = 0;
        public TicketRest(int max)
        {
            Max = max;
        }
        /// <summary>
        /// 未加锁
        /// </summary>
        /// <param name="num"></param>
        public void SellTicketNoLock(int num)
        {
            while (ticket <= Max)
            {
                if (ticket > Max)
                    break;//这里一定要判断。
                Console.WriteLine($"窗口{num}|售票员{Thread.CurrentThread.ManagedThreadId}:售出第[{ticket}]号车票,剩余{Max-ticket}张");
               ticket++;
               Task.Delay(200);
            }
        }
        /// <summary>
        ///加锁
        /// </summary>
        /// <param name="num"></param>
        public void SellTicketAddLock(int num)
        {
            while (ticket <= Max)
            {
                lock (this)
                {
                    if (ticket > Max)
                        break;//这里一定要判断。
                    Console.WriteLine($"窗口{num}|售票员{Thread.CurrentThread.ManagedThreadId}:售出第[{ticket}]号车票,剩余{Max - ticket}张");
                    ticket++;
                   // Task.Delay(200);
                   Thread.Sleep(100); //模拟耗时操作
                }
            }
        }
    }
View Code

 

LoggerFactory.Instance.Logger_Error(ex); 这是输出异常,可以使用console.writeline替代。

 

 参考资料:

理解C#中的闭包: https://www.cnblogs.com/jiejie_peng/p/3701070.html

浅谈并发与并行(一):http://www.cnblogs.com/yangecnu/p/Something-about-Concurrent-and-Parallel-Programming.html

 

以上是关于多线程系列1:经典卖票的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程:线程同步详解

多线程中的经典例题---卖票

多线程卖票代码

Java多窗口卖票问题详解

Java 多线程实现多个窗口同时卖票

多线程—卖票