C#多線程經(jīng)典示例(吃蘋(píng)果)
本文主要講述了多線程開(kāi)發(fā)中經(jīng)典示例,通過(guò)本示例,可以加深對(duì)多線程的理解。
示例概述:
下面用一個(gè)模擬吃蘋(píng)果的實(shí)例,說(shuō)明C#中多線程的實(shí)現(xiàn)方法。要求開(kāi)發(fā)一個(gè)程序?qū)崿F(xiàn)如下情況:一個(gè)家庭有三個(gè)孩子,爸爸媽媽不斷削蘋(píng)果往盤(pán)子里面放,老大、老二、老三不斷從盤(pán)子里面取蘋(píng)果吃。盤(pán)子的大小有限,最多只能放5個(gè)蘋(píng)果,并且爸媽不能同時(shí)往盤(pán)子里面放蘋(píng)果,媽媽具有優(yōu)先權(quán)。三個(gè)孩子取蘋(píng)果時(shí),盤(pán)子不能為空,三人不能同時(shí)取,老三優(yōu)先權(quán)最高,老大最低。老大吃的最快,取的頻率最高,老二次之。
涉及到知識(shí)點(diǎn):
- 線程Thread 創(chuàng)建并控制線程,設(shè)置其優(yōu)先級(jí)并獲取其狀態(tài)。
- 鎖 lock 用于實(shí)現(xiàn)多線程同步的最直接辦法就是加鎖,它可以把一段代碼定義為互斥段,在一個(gè)時(shí)刻內(nèi)只允許一個(gè)線程進(jìn)入執(zhí)行,而其他線程必須等待。
- 事件EventHandler 聲明一個(gè)事件,用于通知界面做改變
設(shè)計(jì)思路:
- Productor 表示生產(chǎn)者,用于削蘋(píng)果。
- Consumer 表示消費(fèi)者,用于吃蘋(píng)果。
- Dish 盤(pán)子,用于裝蘋(píng)果,做為中間類(lèi)
- EatAppleSmp 的BeginEat()方法,表示開(kāi)始吃蘋(píng)果,啟動(dòng)線程
效果圖如下【爸爸媽媽削蘋(píng)果,孩子吃蘋(píng)果】:
后臺(tái)輸出如下:
Mama放1個(gè)蘋(píng)果 Baba放1個(gè)蘋(píng)果 Dage取蘋(píng)果吃... Erdi取蘋(píng)果吃... Sandi等待取蘋(píng)果 Mama放1個(gè)蘋(píng)果 Sandi取蘋(píng)果吃... Baba放1個(gè)蘋(píng)果 Dage取蘋(píng)果吃... Mama放1個(gè)蘋(píng)果 Baba放1個(gè)蘋(píng)果 Erdi取蘋(píng)果吃... Mama放1個(gè)蘋(píng)果 Baba放1個(gè)蘋(píng)果 Dage取蘋(píng)果吃... Sandi取蘋(píng)果吃... Mama放1個(gè)蘋(píng)果 Baba放1個(gè)蘋(píng)果 Erdi取蘋(píng)果吃... Mama放1個(gè)蘋(píng)果 Baba放1個(gè)蘋(píng)果 Dage取蘋(píng)果吃... Mama放1個(gè)蘋(píng)果 Baba放1個(gè)蘋(píng)果 Sandi取蘋(píng)果吃... Mama放1個(gè)蘋(píng)果 Baba正在等待放入蘋(píng)果 Erdi取蘋(píng)果吃... Baba放1個(gè)蘋(píng)果 Dage取蘋(píng)果吃... Mama放1個(gè)蘋(píng)果 Baba正在等待放入蘋(píng)果 Mama正在等待放入蘋(píng)果 Sandi取蘋(píng)果吃... Baba放1個(gè)蘋(píng)果 Mama正在等待放入蘋(píng)果 Erdi取蘋(píng)果吃... Mama放1個(gè)蘋(píng)果 Dage取蘋(píng)果吃... Baba放1個(gè)蘋(píng)果 Mama正在等待放入蘋(píng)果 Dage取蘋(píng)果吃... Mama放1個(gè)蘋(píng)果 Baba正在等待放入蘋(píng)果 Erdi取蘋(píng)果吃... Baba放1個(gè)蘋(píng)果 Sandi取蘋(píng)果吃... Mama放1個(gè)蘋(píng)果 Baba正在等待放入蘋(píng)果 Dage取蘋(píng)果吃... Baba放1個(gè)蘋(píng)果 Mama正在等待放入蘋(píng)果 Erdi取蘋(píng)果吃... Mama放1個(gè)蘋(píng)果 Baba正在等待放入蘋(píng)果 Sandi取蘋(píng)果吃... Baba放1個(gè)蘋(píng)果 Mama正在等待放入蘋(píng)果 Dage取蘋(píng)果吃... Mama放1個(gè)蘋(píng)果 Baba正在等待放入蘋(píng)果 Mama正在等待放入蘋(píng)果 Erdi取蘋(píng)果吃... Mama放1個(gè)蘋(píng)果 Baba正在等待放入蘋(píng)果 Dage取蘋(píng)果吃... Baba放1個(gè)蘋(píng)果 Mama正在等待放入蘋(píng)果 Sandi取蘋(píng)果吃... Mama放1個(gè)蘋(píng)果 Baba正在等待放入蘋(píng)果 Mama正在等待放入蘋(píng)果 線程 'Mama' (0x1ce0) 已退出,返回值為 0 (0x0)。 線程 'Baba' (0x1888) 已退出,返回值為 0 (0x0)。 Erdi取蘋(píng)果吃... Dage取蘋(píng)果吃... Sandi取蘋(píng)果吃... Dage取蘋(píng)果吃... Erdi取蘋(píng)果吃... Dage等待取蘋(píng)果 Sandi等待取蘋(píng)果 Erdi等待取蘋(píng)果 后臺(tái)輸出
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;//聲明一個(gè)事件,當(dāng)放蘋(píng)果時(shí)觸發(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);//削蘋(píng)果時(shí)間 } 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;//聲明一個(gè)事件,當(dāng)放蘋(píng)果時(shí)觸發(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) { //如果取到蘋(píng)果,則調(diào)用事件,并開(kāi)始吃 if (GetAction != null) { GetAction(this, null); } try { Thread.Sleep(timelong);//吃蘋(píng)果時(shí)間 } catch (ThreadInterruptedException) { } } else { break; } } } } }
Dish代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace DemoSharp.EatApple { /// <summary> /// 盤(pán)子,屬于中間類(lèi) /// </summary> public class Dish { private int f = 5;//表示盤(pán)子中還可以放幾個(gè)蘋(píng)果,最多只能放5個(gè)蘋(píng)果 private int EnabledNum;//可放蘋(píng)果總數(shù) private int n = 0; //表示已經(jīng)放了多少個(gè)蘋(píng)果 private object objGet = new object(); private object objPut = new object(); /// <summary> /// 構(gòu)造函數(shù),初始化Dish對(duì)象 /// </summary> /// <param name="num">表示削夠多少個(gè)蘋(píng)果結(jié)束</param> public Dish(int num) { this.EnabledNum = num; } /// <summary> /// 放蘋(píng)果的方法 /// </summary> /// <param name="name"></param> ///<returns>是否放成功</returns> public bool Put(string name) { lock (this)//同步控制放蘋(píng)果 { bool flag = false; while (f == 0)//蘋(píng)果已滿(mǎn),線程等待 { try { System.Console.WriteLine(name + "正在等待放入蘋(píng)果"); Monitor.Wait(this); } catch (Exception ex) { System.Console.WriteLine(name + "等不及了"); } } if (n < EnabledNum) { f = f - 1;//削完一個(gè)蘋(píng)果放一次 n = n + 1; System.Console.WriteLine(name + "放1個(gè)蘋(píng)果"); flag = true; } Monitor.PulseAll(this); return flag; } } /// <summary> /// 取蘋(píng)果的方法 /// </summary> /// <param name="name"></param> public bool Get(string name) { lock (this)//同步控制取蘋(píng)果 { bool flag = false; while (f == 5) { try { System.Console.WriteLine(name + "等待取蘋(píng)果"); Monitor.Wait(this); } catch (ThreadInterruptedException) { } } if (n <= EnabledNum) { f = f + 1; System.Console.WriteLine(name + "取蘋(píng)果吃..."); 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;//聲明一個(gè)事件,當(dāng)放蘋(píng)果時(shí)觸發(fā)該事件 public EventHandler GetAction;//聲明一個(gè)事件,當(dāng)放蘋(píng)果時(shí)觸發(fā)該事件 /// <summary> /// 開(kāi)始吃蘋(píng)果 /// </summary> public void BeginEat() { Thread th_mother, th_father, th_young, th_middle, th_old;//依次表示媽媽?zhuān)职?,小弟,二弟,大? 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)先級(jí) 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); } } } }
界面類(lèi)代碼如下:
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> /// 頁(yè)面類(lèi) /// </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> /// 啟動(dòng)線程 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnStart_Click(object sender, EventArgs e) { this.m_EatAppleSmp.BeginEat(); } /// <summary> /// 放蘋(píng)果事件 /// </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> /// 吃蘋(píng)果事件 /// </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中添加對(duì)象 /// </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); })); //顯示個(gè)數(shù) lbl.Invoke(new Action(() => { if (string.IsNullOrEmpty(lbl.Text)) { lbl.Text = "0"; } lbl.Text = (int.Parse(lbl.Text) + 1).ToString(); })); } } }
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!
相關(guān)文章
使用C#實(shí)現(xiàn)自己封裝的Modbus工具類(lèi)庫(kù)
Modbus通訊協(xié)議在工控行業(yè)的應(yīng)用是很多的,并且也是上位機(jī)開(kāi)發(fā)的基本技能之一,所以本文主要為大家介紹了如何使用C#封裝一個(gè)Modbus工具類(lèi)庫(kù),需要的可以參考下2024-02-02C#使用委托(delegate)實(shí)現(xiàn)在兩個(gè)form之間傳遞數(shù)據(jù)的方法
這篇文章主要介紹了C#使用委托(delegate)實(shí)現(xiàn)在兩個(gè)form之間傳遞數(shù)據(jù)的方法,涉及C#委托的使用技巧,需要的朋友可以參考下2015-04-04C#中結(jié)構(gòu)體定義并轉(zhuǎn)換字節(jié)數(shù)組詳解
在寫(xiě)C#TCP通信程序時(shí),發(fā)送數(shù)據(jù)時(shí),只能發(fā)送byte數(shù)組,處理起來(lái)比較麻煩不說(shuō),如果是和VC6.0等寫(xiě)的程序通信的話(huà),很多的都是傳送結(jié)構(gòu)體,在VC6.0中可以很方便的把一個(gè)char[]數(shù)組轉(zhuǎn)換為一個(gè)結(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一段時(shí)間了,現(xiàn)在就分享一下我的學(xué)習(xí)過(guò)程,本文主要介紹了c#中WinForm用OpencvSharp實(shí)現(xiàn)ROI區(qū)域提取的示例,具有一定的參考價(jià)值,感興趣的可以了解一下2022-05-05