C#多線程經(jīng)典示例(吃蘋果)
本文主要講述了多線程開發(fā)中經(jīng)典示例,通過本示例,可以加深對多線程的理解。
示例概述:
下面用一個模擬吃蘋果的實(shí)例,說明C#中多線程的實(shí)現(xiàn)方法。要求開發(fā)一個程序?qū)崿F(xiàn)如下情況:一個家庭有三個孩子,爸爸媽媽不斷削蘋果往盤子里面放,老大、老二、老三不斷從盤子里面取蘋果吃。盤子的大小有限,最多只能放5個蘋果,并且爸媽不能同時往盤子里面放蘋果,媽媽具有優(yōu)先權(quán)。三個孩子取蘋果時,盤子不能為空,三人不能同時取,老三優(yōu)先權(quán)最高,老大最低。老大吃的最快,取的頻率最高,老二次之。
涉及到知識點(diǎn):
- 線程Thread 創(chuàng)建并控制線程,設(shè)置其優(yōu)先級并獲取其狀態(tài)。
- 鎖 lock 用于實(shí)現(xiàn)多線程同步的最直接辦法就是加鎖,它可以把一段代碼定義為互斥段,在一個時刻內(nèi)只允許一個線程進(jìn)入執(zhí)行,而其他線程必須等待。
- 事件EventHandler 聲明一個事件,用于通知界面做改變
設(shè)計思路:
- Productor 表示生產(chǎn)者,用于削蘋果。
- Consumer 表示消費(fèi)者,用于吃蘋果。
- 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> /// 生產(chǎn)者 /// </summary> public class Productor { private Dish dish; private string name; public string Name { get { return name; } set { name = value; } } public EventHandler PutAction;//聲明一個事件,當(dāng)放蘋果時觸發(fā)該事件 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> /// 消費(fèi)者 /// </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;//聲明一個事件,當(dāng)放蘋果時觸發(fā)該事件 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) { //如果取到蘋果,則調(diào)用事件,并開始吃 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;//可放蘋果總數(shù) private int n = 0; //表示已經(jīng)放了多少個蘋果 private object objGet = new object(); private object objPut = new object(); /// <summary> /// 構(gòu)造函數(shù),初始化Dish對象 /// </summary> /// <param name="num">表示削夠多少個蘋果結(jié)束</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;//聲明一個事件,當(dāng)放蘋果時觸發(fā)該事件 public EventHandler GetAction;//聲明一個事件,當(dāng)放蘋果時觸發(fā)該事件 /// <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;//設(shè)置優(yōu)先級 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); })); //顯示個數(shù) lbl.Invoke(new Action(() => { if (string.IsNullOrEmpty(lbl.Text)) { lbl.Text = "0"; } lbl.Text = (int.Parse(lbl.Text) + 1).ToString(); })); } } }
以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!
相關(guān)文章
使用C#實(shí)現(xiàn)自己封裝的Modbus工具類庫
Modbus通訊協(xié)議在工控行業(yè)的應(yīng)用是很多的,并且也是上位機(jī)開發(fā)的基本技能之一,所以本文主要為大家介紹了如何使用C#封裝一個Modbus工具類庫,需要的可以參考下2024-02-02C#使用委托(delegate)實(shí)現(xiàn)在兩個form之間傳遞數(shù)據(jù)的方法
這篇文章主要介紹了C#使用委托(delegate)實(shí)現(xiàn)在兩個form之間傳遞數(shù)據(jù)的方法,涉及C#委托的使用技巧,需要的朋友可以參考下2015-04-04C#中結(jié)構(gòu)體定義并轉(zhuǎn)換字節(jié)數(shù)組詳解
在寫C#TCP通信程序時,發(fā)送數(shù)據(jù)時,只能發(fā)送byte數(shù)組,處理起來比較麻煩不說,如果是和VC6.0等寫的程序通信的話,很多的都是傳送結(jié)構(gòu)體,在VC6.0中可以很方便的把一個char[]數(shù)組轉(zhuǎn)換為一個結(jié)構(gòu)體,而在C#卻不能直接把byte數(shù)組轉(zhuǎn)換為結(jié)構(gòu)體,要在C#中發(fā)送結(jié)構(gòu)體,應(yīng)該怎么做呢?2017-11-11C#實(shí)現(xiàn)生成所有不重復(fù)的組合功能示例
這篇文章主要介紹了C#實(shí)現(xiàn)生成所有不重復(fù)的組合功能,涉及C#數(shù)學(xué)運(yùn)算中組合數(shù)運(yùn)算的相關(guān)原理應(yīng)用操作技巧,需要的朋友可以參考下2017-12-12c#中WinForm用OpencvSharp實(shí)現(xiàn)ROI區(qū)域提取的示例
已經(jīng)自學(xué)OpencvSharp一段時間了,現(xiàn)在就分享一下我的學(xué)習(xí)過程,本文主要介紹了c#中WinForm用OpencvSharp實(shí)現(xiàn)ROI區(qū)域提取的示例,具有一定的參考價值,感興趣的可以了解一下2022-05-05