C#多線(xiàn)程經(jīng)典示例(吃蘋(píng)果)
本文主要講述了多線(xiàn)程開(kāi)發(fā)中經(jīng)典示例,通過(guò)本示例,可以加深對(duì)多線(xiàn)程的理解。
示例概述:
下面用一個(gè)模擬吃蘋(píng)果的實(shí)例,說(shuō)明C#中多線(xiàn)程的實(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):
- 線(xiàn)程Thread 創(chuàng)建并控制線(xiàn)程,設(shè)置其優(yōu)先級(jí)并獲取其狀態(tài)。
- 鎖 lock 用于實(shí)現(xiàn)多線(xiàn)程同步的最直接辦法就是加鎖,它可以把一段代碼定義為互斥段,在一個(gè)時(shí)刻內(nèi)只允許一個(gè)線(xiàn)程進(jìn)入執(zhí)行,而其他線(xiàn)程必須等待。
- 事件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)線(xiàn)程
效果圖如下【爸爸媽媽削蘋(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)果 線(xiàn)程 'Mama' (0x1ce0) 已退出,返回值為 0 (0x0)。 線(xiàn)程 '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),線(xià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);//建立線(xiàn)程
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)線(xiàn)程
/// </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-02
C#使用委托(delegate)實(shí)現(xiàn)在兩個(gè)form之間傳遞數(shù)據(jù)的方法
這篇文章主要介紹了C#使用委托(delegate)實(shí)現(xiàn)在兩個(gè)form之間傳遞數(shù)據(jù)的方法,涉及C#委托的使用技巧,需要的朋友可以參考下2015-04-04
C#中結(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-11
C#實(shí)現(xiàn)生成所有不重復(fù)的組合功能示例
這篇文章主要介紹了C#實(shí)現(xiàn)生成所有不重復(fù)的組合功能,涉及C#數(shù)學(xué)運(yùn)算中組合數(shù)運(yùn)算的相關(guān)原理應(yīng)用操作技巧,需要的朋友可以參考下2017-12-12
c#中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

