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)果已滿,線程等待
{
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;//依次表示媽媽,爸爸,小弟,二弟,大哥
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# 使用鼠標(biāo)點(diǎn)擊對(duì)Chart控件實(shí)現(xiàn)數(shù)據(jù)提示效果
這篇文章主要介紹了C# 使用鼠標(biāo)點(diǎn)擊對(duì)Chart控件實(shí)現(xiàn)數(shù)據(jù)提示效果,文章給予上一篇的詳細(xì)內(nèi)容做延伸介紹,需要的小伙伴可任意參考一下2022-08-08
如何獲取C#中方法的執(zhí)行時(shí)間以及其代碼注入詳解
這篇文章主要給大家介紹了關(guān)于如何獲取C#中方法的執(zhí)行時(shí)間以及其代碼注入的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧2018-11-11
C#實(shí)現(xiàn)閃動(dòng)托盤(pán)圖標(biāo)效果的方法
這篇文章主要介紹了C#實(shí)現(xiàn)閃動(dòng)托盤(pán)圖標(biāo)效果的方法,涉及C# ImageList控件的使用技巧,需要的朋友可以參考下2016-06-06
C#通過(guò)經(jīng)緯度計(jì)算2個(gè)點(diǎn)之間距離的實(shí)現(xiàn)代碼
這篇文章主要介紹了C#通過(guò)經(jīng)緯度計(jì)算2個(gè)點(diǎn)之間距離實(shí)現(xiàn)代碼,本文對(duì)實(shí)現(xiàn)原理、經(jīng)緯度基本知識(shí)等一并做了講解,需要的朋友可以參考下2014-08-08
詳解C#如何為某個(gè)方法設(shè)定執(zhí)行超時(shí)時(shí)間
這篇文章主要為大家詳細(xì)介紹一下C#如何為某個(gè)方法設(shè)定執(zhí)行超時(shí)時(shí)間,文中的示例代碼簡(jiǎn)潔易懂,具有一定的借鑒價(jià)值,有需要的小伙伴可以學(xué)習(xí)一下2023-10-10

