多线程之经典吃苹果实例
Posted ShawSir
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程之经典吃苹果实例相关的知识,希望对你有一定的参考价值。
1,事例描述
/** * 一个家庭有三个孩子,爸爸妈妈不断削苹果往盘子里面放,老大、老二、老三不断从盘子里面取苹果吃。 * 盘子的大小有限,最多只能放5个苹果,并且爸妈不能同时往盘子里面放苹果,妈妈具有优先权。=>同时等待,妈妈可以先放 * 三个孩子取苹果时,盘子不能为空,三人不能同时取,老三优先权最高,老大最低。老大吃的最快,取的频率最高,老二次之。 =>同时等待,老三可以先取;老大吃完一个的时间最短 * 知识点: * •线程Thread 创建并控制线程,设置其优先级并获取其状态。 * •锁 lock 用于实现多线程同步的最直接办法就是加锁,它可以把一段代码定义为互斥段,在一个时刻内只允许一个线程进入执行,而其他线程必须等待。 * •事件EventHandler 声明一个事件,用于通知界面做改变 * 设计思路: * •Producer 表示生产者,用于削苹果。 * •Consumer 表示消费者,用于吃苹果。 * •Dish 盘子,用于装苹果,做为中间类 * •EatAppleSmp BeginEat()方法,表示开始吃苹果,启动线程 * * 扩展: * 如果苹果的总数只有一百个?如果兄弟三个各自饭量有限?各自吃了多少个? */
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 + ")"; } }
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;//生产失败 } } }
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; } } }
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); } }
6,问题
生产完成,无剩余苹果的情况下,下一个生产者还是先生产了一个导致最终多生产一个,未找到原因.
以上是关于多线程之经典吃苹果实例的主要内容,如果未能解决你的问题,请参考以下文章