多线程之经典吃苹果实例

时间:2021-10-25 04:51:24

1,事例描述 

多线程之经典吃苹果实例多线程之经典吃苹果实例
    /**
     * 一个家庭有三个孩子,爸爸妈妈不断削苹果往盘子里面放,老大、老二、老三不断从盘子里面取苹果吃。
     * 盘子的大小有限,最多只能放5个苹果,并且爸妈不能同时往盘子里面放苹果,妈妈具有优先权。=>同时等待,妈妈可以先放
     * 三个孩子取苹果时,盘子不能为空,三人不能同时取,老三优先权最高,老大最低。老大吃的最快,取的频率最高,老二次之。 =>同时等待,老三可以先取;老大吃完一个的时间最短
     * 知识点:
     * •线程Thread 创建并控制线程,设置其优先级并获取其状态。
     * •锁 lock 用于实现多线程同步的最直接办法就是加锁,它可以把一段代码定义为互斥段,在一个时刻内只允许一个线程进入执行,而其他线程必须等待。
     * •事件EventHandler 声明一个事件,用于通知界面做改变 
     * 设计思路:
     * •Producer 表示生产者,用于削苹果。
     * •Consumer 表示消费者,用于吃苹果。
     * •Dish 盘子,用于装苹果,做为中间类
     * •EatAppleSmp  BeginEat()方法,表示开始吃苹果,启动线程 
     * 
     * 扩展:
     * 如果苹果的总数只有一百个?如果兄弟三个各自饭量有限?各自吃了多少个?
     */
Description

2,盘子

多线程之经典吃苹果实例多线程之经典吃苹果实例
    /// <summary>
    /// 容器:盘子
    /// </summary>
    class Dish
    {
        private int apples = 0;//苹果的总个数
        private int produced = 0;//已生产的个数
        private int consumed = 0;//已经消费的个数

        private int capacity = 0;//容量
        private int stored = 0;//当前已存放
        private int left = 0;//当前可存放

        public int Apples
        {
            get { return apples; }
        }

        /// <summary>
        /// 构造函数:初始化盘子,这是个空盘子
        /// </summary>
        /// <param name="num">可存放多少个</param>
        public Dish(int num)
        {
            this.capacity = num;
            this.left = num;
            this.stored = 0;
        }
        /// <summary>
        /// 附加构造,苹果总数
        /// </summary>
        /// <param name="apples"></param>
        /// <param name="capa"></param>
        public Dish(int apples, int capa) : this(capa)
        {
            this.apples = apples;
        }
        /// <summary>
        /// 放入苹果
        /// </summary>
        /// <param name="name">生产者</param>
        /// <returns>成功与否</returns>
        public bool Put(string name)
        {
            lock (this)//同步控制放苹果
            {
                //加上苹果个数限制
                if (produced >= this.apples)//如果是"==",一个生产者判断后,另一个生产者判断时,就不等了,应该改成">="
                {
                    Console.ForegroundColor = ConsoleColor.Green;
                    Console.WriteLine(name   "苹果都削完了"   this.ToString());
                    Thread.CurrentThread.Abort();
                }

                bool flag = false;
                while (left == 0)//可存放数量为0,线程等待
                {
                    try
                    {
                        Console.ForegroundColor = ConsoleColor.White;
                        Console.WriteLine(name   "等待放入苹果"   this.ToString());
                        Monitor.Wait(this);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(name   "等不及了:"   ex.Message);
                    }
                }
                //未满
                if (stored < capacity)
                {
                    //放入一个
                    left--;
                    stored  ;
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.WriteLine(name   "放入一个苹果"   this.ToString());
                    flag = true;

                    produced  ;

                }
                Monitor.PulseAll(this);
                return flag;
            }
        }
        /// <summary>
        /// 取出苹果
        /// </summary>
        /// <param name="name">消费者</param>
        /// <returns></returns>
        public bool Get(string name)
        {
            lock (this)//同步控制取苹果
            {
                bool flag = false;
                while (stored == 0)//无剩余,等待生产
                {
                    try
                    {
                        Console.ForegroundColor = ConsoleColor.White;
                        Console.WriteLine(name   "正在等待苹果"   this.ToString());
                        Monitor.Wait(this);
                    }
                    catch (Exception ex) { Console.WriteLine(name   "等不及了:"   ex.Message); }
                }
                if (stored <= capacity)//应该是<=,否则,满了,就不消费了
                {
                    //取出
                    stored--;
                    left  ;
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine(name   "取出一个苹果"   this.ToString());
                    flag = true;

                    consumed  ;
                }
                Monitor.PulseAll(this);

                //应对苹果个数限制:吃完后,结束
                if (consumed == this.apples)
                {
                    Console.ForegroundColor = ConsoleColor.Green;
                    Console.WriteLine("苹果全部都吃完了"   this.ToString());
                    Thread.CurrentThread.Abort();
                }

                return flag;
            }
        }

        public override string ToString()
        {
            return " (总数,容量):("   apples   ","   capacity   ");(现存,可放):"   stored   ","   left   ";(生产,消费):("   produced   ","   consumed   ")";
        }
    }
Dish

3,生产者

多线程之经典吃苹果实例多线程之经典吃苹果实例
    /// <summary>
    /// 生产者
    /// </summary>
    class Producer
    {
        private Dish dish;
        private string name;
        private int timelong;//削一个苹果的时间

        public EventHandler PutAction;//声明一个事件,生产时触发

        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="name">生产者</param>
        /// <param name="dish">盘子</param>>
        /// <param name="timelong">生产时间</param>
        public Producer(string name, Dish dish, int timelong)
        {
            this.name = name; this.dish = dish; this.timelong = timelong;
        }
        /// <summary>
        /// 开始生产
        /// </summary>
        public void Produce()
        {
            while (true)
            {
                bool flag = dish.Put(name);
                if (flag)
                {
                    if (PutAction != null)
                    {
                        PutAction(this, null);
                    }
                    try
                    {
                        Thread.Sleep(timelong);//生产时间
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(name   "削到手指了:"   ex.Message);
                    }
                }
                else break;//生产失败
            }
        }
    }
Producer

4,消费者

多线程之经典吃苹果实例多线程之经典吃苹果实例
    /// <summary>
    /// 消费者
    /// </summary>
    class Consumer
    {
        private string name;//名称
        private Dish dish;//盘子
        private int timelong;//消费一个的时长
        private int limit;//最多消费几个
        private int total = 0;//总共消费数量

        public EventHandler GetAction;//声明事件,取苹果时触发

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public int Total
        {
            get { return total; }
        }

        public Consumer(string name, Dish dish, int timelong)
        {
            this.name = name;
            this.dish = dish;
            this.timelong = timelong;
        }
        public Consumer(string name, Dish dish, int timelong, int limit) : this(name, dish, timelong)
        {
            this.limit = limit;
        }

        public void Consume()
        {
            while (true)
            {
                //加上消费数量限制
                if (total == limit)
                {
                    Console.ForegroundColor = ConsoleColor.Blue;
                    Console.WriteLine(name   "已经吃不下了:最多吃"   limit   "个,已经吃了"   total   "");
                    Thread.CurrentThread.Abort();
                }

                bool flag = dish.Get(name);
                if (flag)
                {////如果取到苹果,则调用事件,并开始吃
                    total  ;

                    if (GetAction != null)
                    {
                        GetAction(this, null);
                    }
                    try { Thread.Sleep(timelong); }//吃苹果时间
                    catch (Exception ex)
                    {
                        Console.WriteLine(name   "咬到舌头了:"   ex.Message);
                    }
                }
                else break;
            }
        }
    }
Consumer

5,实例入口

多线程之经典吃苹果实例多线程之经典吃苹果实例
    /// <summary>
    /// 实例
    /// </summary>
    class EatAppleSmp
    {
        public EventHandler PutAction, GetAction;
        /// <summary>
        /// 开吃
        /// </summary>
        public void BeginEat()
        {
            Thread mum, dad, one, two, three;
            //初始化容器
            Dish dish = new Dish(100, 10);
            //定义生产者
            Producer
                p_mum = new Producer("妈妈", dish, 500),
                p_dad = new Producer("爸爸", dish, 600);
            //注册生产事件
            p_mum.PutAction  = PutMethod;
            p_dad.PutAction  = PutMethod;
            //定义消费者
            Consumer
                c_one = new Consumer("老大", dish, 800, 50),
                c_two = new Consumer("老二", dish, 900, 30),
                c_three = new Consumer("老三", dish, 1000, 20);
            //注册消费事件
            c_one.GetAction  = GetMethod;
            c_two.GetAction  = GetMethod;
            c_three.GetAction  = GetMethod;
            //定义各角色线程
            mum = new Thread(new ThreadStart(p_mum.Produce));
            mum.Name = "妈妈";
            dad = new Thread(new ThreadStart(p_dad.Produce));
            dad.Name = "爸爸";
            one = new Thread(new ThreadStart(c_one.Consume));
            one.Name = "老大";
            two = new Thread(new ThreadStart(c_two.Consume));
            two.Name = "老二";
            three = new Thread(new ThreadStart(c_three.Consume));
            three.Name = "老三";
            //优先级
            mum.Priority = ThreadPriority.Highest;
            dad.Priority = ThreadPriority.Normal;
            one.Priority = ThreadPriority.Lowest;
            two.Priority = ThreadPriority.Normal;
            three.Priority = ThreadPriority.Highest;
            //启动线程
            mum.Start();
            dad.Start();
            one.Start();
            two.Start();
            three.Start();
        }

        private void PutMethod(object sender, EventArgs e)
        {
            if (PutAction != null)
                PutAction(sender, e);
        }
        private void GetMethod(object sender, EventArgs e)
        {
            if (GetAction != null)
                GetAction(sender, e);
        }
    }
Main

6,问题

 生产完成,无剩余苹果的情况下,下一个生产者还是先生产了一个导致最终多生产一个,未找到原因.

多线程之经典吃苹果实例

多线程之经典吃苹果实例

多线程之经典吃苹果实例

多线程之经典吃苹果实例

 

参考