本文主要讲述了多线程开发中经典示例,通过本示例,可以加深对多线程的理解。
示例概述:
下面用一个模拟吃苹果的实例,说明C#中多线程的实现方法。要求开发一个程序实现如下情况:一个家庭有三个孩子,爸爸妈妈不断削苹果往盘子里面放,老大、老二、老三不断从盘子里面取苹果吃。盘子的大小有限,最多只能放5个苹果,并且爸妈不能同时往盘子里面放苹果,妈妈具有优先权。三个孩子取苹果时,盘子不能为空,三人不能同时取,老三优先权最高,老大最低。老大吃的最快,取的频率最高,老二次之。.
涉及知识点:
-
线程Thread 创建并控制线程,设置其优先级并获取其状态。
-
锁 lock 用于实现多线程同步的最直接办法就是加锁,它可以把一段代码定义为互斥段,在一个时刻内只允许一个线程进入执行,而其他线程必须等待。
-
事件EventHandler 声明一个事件,用于通知界面做改变
设计思路:
-
Productor 表示生产者,用于削苹果。
-
Consumer 表示消费者,用于吃苹果。
-
Dish 盘子,用于装苹果,作为中间类
-
EatAppleSmp 的BeginEat()方法,表示开始吃苹果,启动线程
效果图
如下【爸爸妈妈削苹果,孩子吃苹果】:

核心算法
后台输出如下:
Mama放1个苹果Baba放1个苹果Dage取苹果吃...Erdi取苹果吃...Sandi等待取苹果Mama放1个苹果Sandi取苹果吃...Baba放1个苹果Dage取苹果吃...Mama放1个苹果Baba放1个苹果Erdi取苹果吃...Mama放1个苹果Baba放1个苹果Dage取苹果吃...Sandi取苹果吃...Mama放1个苹果Baba放1个苹果Erdi取苹果吃...Mama放1个苹果Baba放1个苹果Dage取苹果吃...Mama放1个苹果Baba放1个苹果Sandi取苹果吃...Mama放1个苹果Baba正在等待放入苹果Erdi取苹果吃...Baba放1个苹果Dage取苹果吃...Mama放1个苹果Baba正在等待放入苹果Mama正在等待放入苹果Sandi取苹果吃...Baba放1个苹果Mama正在等待放入苹果Erdi取苹果吃...Mama放1个苹果Dage取苹果吃...Baba放1个苹果Mama正在等待放入苹果Dage取苹果吃...Mama放1个苹果Baba正在等待放入苹果Erdi取苹果吃...Baba放1个苹果Sandi取苹果吃...Mama放1个苹果Baba正在等待放入苹果Dage取苹果吃...Baba放1个苹果Mama正在等待放入苹果Erdi取苹果吃...Mama放1个苹果Baba正在等待放入苹果Sandi取苹果吃...Baba放1个苹果Mama正在等待放入苹果Dage取苹果吃...Mama放1个苹果Baba正在等待放入苹果Mama正在等待放入苹果Erdi取苹果吃...Mama放1个苹果Baba正在等待放入苹果Dage取苹果吃...Baba放1个苹果Mama正在等待放入苹果Sandi取苹果吃...Mama放1个苹果Baba正在等待放入苹果Mama正在等待放入苹果线程 'Mama' (0x1ce0) 已退出,返回值为 0 (0x0)。线程 'Baba' (0x1888) 已退出,返回值为 0 (0x0)。Erdi取苹果吃...Dage取苹果吃...Sandi取苹果吃...Dage取苹果吃...Erdi取苹果吃...Dage等待取苹果Sandi等待取苹果Erdi等待取苹果后台输出
Productor 代码如下:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;namespace DemoSharp.EatApple{/// <summary>/// 生产者/// </summary>public class Productor{private Dish dish;private string name;public string Name{get { return name; }set { name = value; }}public EventHandler PutAction;//声明一个事件,当放苹果时触发该事件public Productor(string name, Dish dish){this.name = name;this.dish = dish;}public void run(){while (true){bool flag= dish.Put(name);if (flag){if (PutAction != null){PutAction(this, null);}try{Thread.Sleep(600);//削苹果时间}catch (Exception ex){}}else {break;}}}}}
Consumer代码如下:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;namespace DemoSharp.EatApple{/// <summary>/// 消费者/// </summary>public class Consumer{private string name;public string Name{get { return name; }set { name = value; }}private Dish dish;private int timelong;public EventHandler GetAction;//声明一个事件,当放苹果时触发该事件public Consumer(string name, Dish dish, int timelong){this.name = name;this.dish = dish;this.timelong = timelong;}public void run(){while (true){bool flag= dish.Get(name);if (flag){//如果取到苹果,则调用事件,并开始吃if (GetAction != null){GetAction(this, null);}try{Thread.Sleep(timelong);//吃苹果时间}catch (ThreadInterruptedException){}}else {break;}}}}}
Dish代码如下:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;namespace DemoSharp.EatApple{/// <summary>/// 盘子,属于中间类/// </summary>public class Dish{private int f = 5;//表示盘子中还可以放几个苹果,最多只能放5个苹果private int EnabledNum;//可放苹果总数private int n = 0; //表示已经放了多少个苹果private object objGet = new object();private object objPut = new object();/// <summary>/// 构造函数,初始化Dish对象/// </summary>/// <param name="num">表示削够多少个苹果结束</param>public Dish(int num){this.EnabledNum = num;}/// <summary>/// 放苹果的方法/// </summary>/// <param name="name"></param>///<returns>是否放成功</returns>public bool Put(string name){lock (this)//同步控制放苹果{bool flag = false;while (f == 0)//苹果已满,线程等待{try{System.Console.WriteLine(name + "正在等待放入苹果");Monitor.Wait(this);}catch (Exception ex){System.Console.WriteLine(name + "等不及了");}}if (n < EnabledNum){f = f - 1;//削完一个苹果放一次n = n + 1;System.Console.WriteLine(name + "放1个苹果");flag = true;}Monitor.PulseAll(this);return flag;}}/// <summary>/// 取苹果的方法/// </summary>/// <param name="name"></param>public bool Get(string name){lock (this)//同步控制取苹果{bool flag = false;while (f == 5){try{System.Console.WriteLine(name + "等待取苹果");Monitor.Wait(this);}catch (ThreadInterruptedException) { }}if (n <= EnabledNum){f = f + 1;System.Console.WriteLine(name + "取苹果吃...");flag = true;}Monitor.PulseAll(this);return flag;}}}}
EatAppleSmp代码如下:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;namespace DemoSharp.EatApple{public class EatAppleSmp{public EventHandler PutAction;//声明一个事件,当放苹果时触发该事件public EventHandler GetAction;//声明一个事件,当放苹果时触发该事件/// <summary>/// 开始吃苹果/// </summary>public void BeginEat(){Thread th_mother, th_father, th_young, th_middle, th_old;//依次表示妈妈,爸爸,小弟,二弟,大哥Dish dish = new Dish(30);Productor mother = new Productor("Mama", dish);//建立线程mother.PutAction += PutActionMethod;Productor father = new Productor("Baba", dish);father.PutAction += PutActionMethod;Consumer old = new Consumer("Dage", dish, 1200);old.GetAction += GetActionMethod;Consumer middle = new Consumer("Erdi", dish, 1500);middle.GetAction += GetActionMethod;Consumer young = new Consumer("Sandi", dish, 1800);young.GetAction += GetActionMethod;th_mother = new Thread(new ThreadStart(mother.run));th_mother.Name = "Mama";th_father = new Thread(new ThreadStart(father.run));th_father.Name = "Baba";th_old = new Thread(new ThreadStart(old.run));th_old.Name = "Dage";th_middle = new Thread(new ThreadStart(middle.run));th_middle.Name = "Erdi";th_young = new Thread(new ThreadStart(young.run));th_young.Name = "Sandi";th_mother.Priority = ThreadPriority.Highest;//设置优先级th_father.Priority = ThreadPriority.Normal;th_old.Priority = ThreadPriority.Lowest;th_middle.Priority = ThreadPriority.Normal;th_young.Priority = ThreadPriority.Highest;th_mother.Start();th_father.Start();th_old.Start();th_middle.Start();th_young.Start();}private void GetActionMethod(object sender,EventArgs e){if (GetAction != null){GetAction(sender, e);}}private void PutActionMethod(object sender, EventArgs e){if (PutAction != null){PutAction(sender, e);}}}}
界面类代码如下:
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using DemoSharp.EatApple;namespace DemoSharp{/// <summary>/// 页面类/// </summary>public partial class EatAppleForm : Form{private EatAppleSmp m_EatAppleSmp = new EatAppleSmp();public EatAppleForm(){InitializeComponent();InitView();m_EatAppleSmp.PutAction += PutActionMethod;m_EatAppleSmp.GetAction += GetActionMethod;}/// <summary>/// 初始化GroupBox/// </summary>private void InitView(){this.gbBaba.Controls.Clear();this.gbMama.Controls.Clear();this.gbDage.Controls.Clear();this.gbErdi.Controls.Clear();this.gbSandi.Controls.Clear();}/// <summary>/// 启动线程/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnStart_Click(object sender, EventArgs e){this.m_EatAppleSmp.BeginEat();}/// <summary>/// 放苹果事件/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void PutActionMethod(object sender, EventArgs e){Productor p = sender as Productor;if (p != null){if (p.Name == "Baba"){AddItemToGroupBox(this.gbBaba, this.lblBaba);}if (p.Name == "Mama"){AddItemToGroupBox(this.gbMama, this.lblMama);}}}/// <summary>/// 吃苹果事件/// </summary>/// <param name="sender"></param>/// <param name="e"></param>public void GetActionMethod(object sender, EventArgs e){Consumer c = sender as Consumer;if (c != null){if (c.Name == "Dage"){AddItemToGroupBox(this.gbDage, this.lblDage);}if (c.Name == "Erdi"){AddItemToGroupBox(this.gbErdi, this.lblErdi);}if (c.Name == "Sandi"){AddItemToGroupBox(this.gbSandi, this.lblSandi);}}}/// <summary>/// 往指定的GroupBox中添加对象/// </summary>/// <param name="gbView"></param>/// <param name="lbl"></param>private void AddItemToGroupBox(GroupBox gbView,Label lbl){gbView.Invoke(new Action(() =>{PictureBox p = new PictureBox();p.Width = 20;p.Height = 20;p.Dock = DockStyle.Left;p.Image = this.imgLst01.Images[0];p.Margin = new Padding(2);gbView.Controls.Add(p);}));//显示个数lbl.Invoke(new Action(() => {if (string.IsNullOrEmpty(lbl.Text)){lbl.Text = "0";}lbl.Text = (int.Parse(lbl.Text) + 1).ToString();}));}}}