本文主要講述了多線程開發中經典樣本,通過本樣本,可以加深對多線程的理解。
樣本概述:
下面用一個類比吃蘋果的執行個體,說明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.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"; 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; } } } } } } lbl.Text = (int.Parse(lbl.Text) + 1).ToString(); })); } } }
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(); })); } } }