多线程系列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);
                }
            }
        }

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); //模拟耗时操作
                }
            }
        }
    }

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

原文地址:https://www.cnblogs.com/wtujvk/p/8859095.html

时间: 2024-07-30 04:50:10

多线程系列1:经典卖票的相关文章

多线程之多窗口卖票&amp;线程之间的通信

案例一:使用多线程完成三个窗口卖票(不能出现重复卖票以及负数票) 卖票程序SellTicket 这里使用Lock类中的方法实现加锁和释放锁! package cn.itcast.thread2; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class SellTicket implements Runnable { private int ticke

Java多线程练习:ticket卖票程序

/*需求:简单的卖票程序多个窗口买票 */ class Ticket extends Thread{    private static int tick=100;    public void run()    {        while(true)        {            if(tick>0)            {                System.out.println(Thread.currentThread().getName()+"sale:--

多线程通讯轮询卖票了解一下

package com.xp.test; import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock; public class ABC_Lock { public static void main(String[] args) { Print print = new Print(); // ne

java多线程实现卖票小程序

1 package shb.java.demo; 2 /** 3 * 多线程测试卖票小程序. 4 * @Package:shb.java.demo 5 * @Description: 6 * @author shaobn 7 * @Date 2015-9-2下午7:49:53 8 */ 9 public class TestSyn { 10 public static void main(String[] args) { 11 //此注释为实现方式一 12 /*TicketDemo td = n

java多线程实现卖票程序

本文采用java多线程实现了模拟车站多个车票卖票的功能. 关键词:java多线程 共享变量 实现runnable接口 volatile  线程同步. 代码如下 Ticket类 package ex7_TicketSaler; /*同一对象的多个线程thread0/1/2,对共享变量count的操作,需要将count的值声明为volatile * 并且因为多个线程操作的是同一个对象ticket,因此count是资源共享的 * */ public class Ticket implements Ru

多线程(多窗口卖票例子)

实现多线程的方式: 实现多线程的方式有多种,这里只列举两种常用的,而第一种继承Thread的方式无法实现多窗口卖票. 一,继承Thread方式: 特点:多线程多实例,无法实现资源的共享. 例子: 1 package com.demo.study.multithreading; 2 3 public class MyThread extends Thread{ 4 5 private int i = 10; 6 // 可以自行定义锁,也可以使用实例的锁 7 Object mutex = new O

实现多线程的两种方式,卖票场景,亲测可用

写在开始 卖票场景: 多线程共同卖票,总票数在多个卖票窗口共享 实现方式: 1继承Thread类: 2实现Runnable接口 正文开始 方式1 Thread继承 package com.example.demo; /** * @Description: * @Author:tianminghai * @Date:2:55 PM 2018/10/24 */ public class ThreadDemo extends Thread { private static int ticket = 1

多线程卖票代码

package com.loaderman.syn; public class Demo_Ticket { /** * 需求:铁路售票,一共100张,通过四个窗口卖完. */ public static void main(String[] args) { new Ticket().start(); new Ticket().start(); new Ticket().start(); new Ticket().start(); } } class Ticket extends Thread {

java 多线程之卖票两种方式

1.通过extends Thread 1 /* 2 需求:简单的卖票,多个窗口同时买票 3 (共用资源) 4 创建线程的第二种方式:实现Runnable接口 5 6 步骤: 7 1,定义类实现Runnable接口 8 2,覆盖/重写Runnable接口中的run方法 9 将将线程要运行的代码存放在该run方法中 10 3,通过Thread类建立线程对象 11 4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数 12 为什么要将Runnable接口的子类对象传递给Thr