C#多线程经典示例(吃苹果)

 更新时间:2020年6月25日 11:21  点击:1891

本文主要讲述了多线程开发中经典示例,通过本示例,可以加深对多线程的理解。

示例概述:

下面用一个模拟吃苹果的实例,说明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.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;
    }
   }
  }
 }
}

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();
   }));
  }
 }
}

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持猪先飞!

[!--infotagslink--]

相关文章

  • C# WinForm多线程解决界面卡死问题的完美解决方案,使用BeginInvoke

    问题描述:当我们的界面需要在程序运行中不断更新数据时,当一个textbox的数据需要变化时,为了让程序执行中不出现界面卡死的现像,最好的方法就是多线程来解决一个主线程来创建界...2020-06-24
  • C#实现简单的登录界面

    我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
  • 浅谈C# 字段和属性

    这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
  • C#中截取字符串的的基本方法详解

    这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
  • C#连接SQL数据库和查询数据功能的操作技巧

    本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
  • C#实现简单的Http请求实例

    这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
  • C#中new的几种用法详解

    本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
  • 使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)

    这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#开发Windows窗体应用程序的简单操作步骤

    这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
  • C#从数据库读取图片并保存的两种方法

    这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
  • C#和JavaScript实现交互的方法

    最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
  • 经典实例讲解C#递归算法

    这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • 轻松学习C#的基础入门

    轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
  • C#变量命名规则小结

    本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
  • c#中(&&,||)与(&,|)的区别详解

    这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • C#绘制曲线图的方法

    这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • C# 中如何取绝对值函数

    本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
  • c#自带缓存使用方法 c#移除清理缓存

    这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
  • C#学习笔记- 随机函数Random()的用法详解

    下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25