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

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

示例概述:

  下面用一个模拟吃苹果的实例,说明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 代码如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading;
 6
 7 namespace DemoSharp.EatApple
 8 {
 9     /// <summary>
10     /// 生产者
11     /// </summary>
12     public class Productor
13     {
14         private Dish dish;
15         private string name;
16
17         public string Name
18         {
19             get { return name; }
20             set { name = value; }
21         }
22
23         public EventHandler PutAction;//声明一个事件,当放苹果时触发该事件
24
25         public Productor(string name, Dish dish)
26         {
27             this.name = name;
28             this.dish = dish;
29         }
30         public void run()
31         {
32             while (true)
33             {
34                 bool flag= dish.Put(name);
35                 if (flag)
36                 {
37                     if (PutAction != null)
38                     {
39                         PutAction(this, null);
40                     }
41                     try
42                     {
43                         Thread.Sleep(600);//削苹果时间
44                     }
45                     catch (Exception ex)
46                     {
47
48                     }
49                 }
50                 else {
51                     break;
52                 }
53             }
54         }
55     }
56 }

Consumer代码如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading;
 6
 7 namespace DemoSharp.EatApple
 8 {
 9     /// <summary>
10     /// 消费者
11     /// </summary>
12     public class Consumer
13     {
14         private string name;
15
16         public string Name
17         {
18             get { return name; }
19             set { name = value; }
20         }
21         private Dish dish;
22         private int timelong;
23
24         public EventHandler GetAction;//声明一个事件,当放苹果时触发该事件
25
26         public Consumer(string name, Dish dish, int timelong)
27         {
28             this.name = name;
29             this.dish = dish;
30             this.timelong = timelong;
31         }
32         public void run()
33         {
34             while (true)
35             {
36                 bool flag=  dish.Get(name);
37                 if (flag)
38                 {
39                     //如果取到苹果,则调用事件,并开始吃
40                     if (GetAction != null)
41                     {
42                         GetAction(this, null);
43                     }
44                     try
45                     {
46                         Thread.Sleep(timelong);//吃苹果时间
47                     }
48                     catch (ThreadInterruptedException)
49                     {
50                     }
51                 }
52                 else {
53                     break;
54                 }
55             }
56         }
57     }
58 }

Dish代码如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading;
 6
 7 namespace DemoSharp.EatApple
 8 {
 9     /// <summary>
10     /// 盘子,属于中间类
11     /// </summary>
12     public class Dish
13     {
14         private int f = 5;//表示盘子中还可以放几个苹果,最多只能放5个苹果
15
16         private int EnabledNum;//可放苹果总数
17
18         private int n = 0; //表示已经放了多少个苹果
19
20         private object objGet = new object();
21
22         private object objPut = new object();
23
24         /// <summary>
25         /// 构造函数,初始化Dish对象
26         /// </summary>
27         /// <param name="num">表示削够多少个苹果结束</param>
28         public Dish(int num)
29         {
30             this.EnabledNum = num;
31         }
32         /// <summary>
33         /// 放苹果的方法
34         /// </summary>
35         /// <param name="name"></param>
36         ///<returns>是否放成功</returns>
37         public bool Put(string name)
38         {
39             lock (this)//同步控制放苹果
40             {
41                 bool flag = false;
42
43                 while (f == 0)//苹果已满,线程等待
44                 {
45                     try
46                     {
47                         System.Console.WriteLine(name + "正在等待放入苹果");
48                         Monitor.Wait(this);
49                     }
50                     catch (Exception ex)
51                     {
52                         System.Console.WriteLine(name + "等不及了");
53                     }
54                 }
55                 if (n < EnabledNum)
56                 {
57                     f = f - 1;//削完一个苹果放一次
58                     n = n + 1;
59                     System.Console.WriteLine(name + "放1个苹果");
60                     flag = true;
61                 }
62                 Monitor.PulseAll(this);
63                 return flag;
64             }
65         }
66
67         /// <summary>
68         /// 取苹果的方法
69         /// </summary>
70         /// <param name="name"></param>
71         public bool Get(string name)
72         {
73             lock (this)//同步控制取苹果
74             {
75                 bool flag = false;
76                 while (f == 5)
77                 {
78                     try
79                     {
80                         System.Console.WriteLine(name + "等待取苹果");
81                         Monitor.Wait(this);
82                     }
83                     catch (ThreadInterruptedException) { }
84                 }
85                 if (n <= EnabledNum)
86                 {
87                     f = f + 1;
88                     System.Console.WriteLine(name + "取苹果吃...");
89                     flag = true;
90                 }
91                 Monitor.PulseAll(this);
92                 return flag;
93             }
94
95         }
96     }
97 }

EatAppleSmp代码如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading;
 6
 7 namespace DemoSharp.EatApple
 8 {
 9     public class EatAppleSmp
10     {
11         public EventHandler PutAction;//声明一个事件,当放苹果时触发该事件
12
13         public EventHandler GetAction;//声明一个事件,当放苹果时触发该事件
14
15         /// <summary>
16         /// 开始吃苹果
17         /// </summary>
18         public void BeginEat()
19         {
20             Thread th_mother, th_father, th_young, th_middle, th_old;//依次表示妈妈,爸爸,小弟,二弟,大哥
21             Dish dish = new Dish(30);
22             Productor mother = new Productor("Mama", dish);//建立线程
23             mother.PutAction += PutActionMethod;
24             Productor father = new Productor("Baba", dish);
25             father.PutAction += PutActionMethod;
26             Consumer old = new Consumer("Dage", dish, 1200);
27             old.GetAction += GetActionMethod;
28             Consumer middle = new Consumer("Erdi", dish, 1500);
29             middle.GetAction += GetActionMethod;
30             Consumer young = new Consumer("Sandi", dish, 1800);
31             young.GetAction += GetActionMethod;
32             th_mother = new Thread(new ThreadStart(mother.run));
33             th_mother.Name = "Mama";
34             th_father = new Thread(new ThreadStart(father.run));
35             th_father.Name = "Baba";
36             th_old = new Thread(new ThreadStart(old.run));
37             th_old.Name = "Dage";
38             th_middle = new Thread(new ThreadStart(middle.run));
39             th_middle.Name = "Erdi";
40             th_young = new Thread(new ThreadStart(young.run));
41             th_young.Name = "Sandi";
42             th_mother.Priority = ThreadPriority.Highest;//设置优先级
43             th_father.Priority = ThreadPriority.Normal;
44             th_old.Priority = ThreadPriority.Lowest;
45             th_middle.Priority = ThreadPriority.Normal;
46             th_young.Priority = ThreadPriority.Highest;
47             th_mother.Start();
48             th_father.Start();
49             th_old.Start();
50             th_middle.Start();
51             th_young.Start();
52         }
53
54         private void GetActionMethod(object sender,EventArgs e)
55         {
56             if (GetAction != null)
57             {
58                 GetAction(sender, e);
59             }
60         }
61
62         private void PutActionMethod(object sender, EventArgs e)
63         {
64             if (PutAction != null)
65             {
66                 PutAction(sender, e);
67             }
68         }
69     }
70 }

界面类代码如下:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Windows.Forms;
  9 using DemoSharp.EatApple;
 10
 11 namespace DemoSharp
 12 {
 13     /// <summary>
 14     /// 页面类
 15     /// </summary>
 16     public partial class EatAppleForm : Form
 17     {
 18         private EatAppleSmp m_EatAppleSmp = new EatAppleSmp();
 19
 20         public EatAppleForm()
 21         {
 22             InitializeComponent();
 23             InitView();
 24             m_EatAppleSmp.PutAction += PutActionMethod;
 25             m_EatAppleSmp.GetAction += GetActionMethod;
 26         }
 27
 28         /// <summary>
 29         /// 初始化GroupBox
 30         /// </summary>
 31         private void InitView()
 32         {
 33             this.gbBaba.Controls.Clear();
 34             this.gbMama.Controls.Clear();
 35             this.gbDage.Controls.Clear();
 36             this.gbErdi.Controls.Clear();
 37             this.gbSandi.Controls.Clear();
 38         }
 39
 40         /// <summary>
 41         /// 启动线程
 42         /// </summary>
 43         /// <param name="sender"></param>
 44         /// <param name="e"></param>
 45         private void btnStart_Click(object sender, EventArgs e)
 46         {
 47             this.m_EatAppleSmp.BeginEat();
 48         }
 49
 50         /// <summary>
 51         /// 放苹果事件
 52         /// </summary>
 53         /// <param name="sender"></param>
 54         /// <param name="e"></param>
 55         private void PutActionMethod(object sender, EventArgs e)
 56         {
 57             Productor p = sender as Productor;
 58             if (p != null)
 59             {
 60                 if (p.Name == "Baba")
 61                 {
 62                     AddItemToGroupBox(this.gbBaba, this.lblBaba);
 63                 }
 64                 if (p.Name == "Mama")
 65                 {
 66                     AddItemToGroupBox(this.gbMama, this.lblMama);
 67                 }
 68             }
 69         }
 70
 71         /// <summary>
 72         /// 吃苹果事件
 73         /// </summary>
 74         /// <param name="sender"></param>
 75         /// <param name="e"></param>
 76         public void GetActionMethod(object sender, EventArgs e)
 77         {
 78             Consumer c = sender as Consumer;
 79             if (c != null)
 80             {
 81                 if (c.Name == "Dage")
 82                 {
 83                     AddItemToGroupBox(this.gbDage, this.lblDage);
 84                 }
 85                 if (c.Name == "Erdi")
 86                 {
 87                     AddItemToGroupBox(this.gbErdi, this.lblErdi);
 88                 }
 89                 if (c.Name == "Sandi")
 90                 {
 91                     AddItemToGroupBox(this.gbSandi, this.lblSandi);
 92                 }
 93             }
 94         }
 95
 96         /// <summary>
 97         /// 往指定的GroupBox中添加对象
 98         /// </summary>
 99         /// <param name="gbView"></param>
100         /// <param name="lbl"></param>
101         private void AddItemToGroupBox(GroupBox gbView,Label lbl)
102         {
103             gbView.Invoke(new Action(() =>
104             {
105                 PictureBox p = new PictureBox();
106                 p.Width = 20;
107                 p.Height = 20;
108                 p.Dock = DockStyle.Left;
109                 p.Image = this.imgLst01.Images[0];
110                 p.Margin = new Padding(2);
111                 gbView.Controls.Add(p);
112
113             }));
114             //显示个数
115             lbl.Invoke(new Action(() => {
116                 if (string.IsNullOrEmpty(lbl.Text))
117                 {
118                     lbl.Text = "0";
119                 }
120                 lbl.Text = (int.Parse(lbl.Text) + 1).ToString();
121             }));
122         }
123     }
124 }

时间: 2024-10-26 23:13:16

C# 多线程经典示例 吃苹果的相关文章

进程同步经典示例 多线程上篇(五)

同步回顾 进程同步控制有多种方式:算法.硬件.信号量.管程 这些方式可以认为就是同步的工具(方法.函数) 比如信号量机制中的wait(S) 和 signal(S) ,就相当于是两个方法调用. 调用wait(S)就会申请这个资源,否则就会等待(进入等待队列):调用signal(S)就会释放资源(或一并唤醒等待队列中的某个): 在梳理同步问题的解决思路时,只需要合理安排方法调用即可,底层的实现细节不需要关注. 接下来以这种套路,看一下借助与不同的同步方式“算法.硬件.信号量.管程”这一“API”,如

多线程经典问题2——子线程之间交替问题

编写一个程序.开启3个线程,这3个线程的ID分别为A.B.C,每一个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示:如:ABCABC-.依次递推. 这个问题仍旧是线程间的交替控制问题,分析方法还是能够同上一篇"多线程经典问题1--主线程子线程交替问题". 详细代码例如以下: package treadgame; /** * 线程间的交流1 * 编写一个程序,开启3个线程,这3个线程的ID分别为A.B.C, * 每一个线程将自己的ID在屏幕上打印10遍, * 要求

java多线程经典案例

/** * 典型案例:子线程执行10次,主线程执行100次,两者交替50次. */ package cn.itcast.lesson4; public class TestWaitNotify { public static void main(String[] args){ final Business business= new Business(); new Thread( new Runnable() { public void run() { for(int i=1;i<=50;i++

gdb 调试openmp多线程程序 示例介绍

 首先一个简单程序源代码: 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <omp.h> 4 int main(int argc, char * argv[]){ 5 int i, k,m,n; 6 printf("i=%d,threads_num=%d\n",i, omp_get_num_threads()); 7 printf("i=%d,thread_id=%d\n&quo

Android(java)回调函数经典示例

回调函数经典示例 1.使用java回调函数来实现一个测试函数运行时间的工具类 public class TestObject { /** * 一个用来被测试的方法,进行了一个比较耗时的循环 */ public static void testMethod(){ for ( int i= 0 ; i< 100000000 ; i++){ } } /** * 一个简单的测试方法执行时间的方法 */ public void testTime(){ long begin = System.current

[Vijos 1676] 陶陶吃苹果

Description curimit知道陶陶很喜欢吃苹果.于是curimit准备在陶陶生日的时候送给他一棵苹果树. curimit准备了一棵这样的苹果树作为生日礼物:这棵苹果树有n个节点,每个节点上有c[i]个苹果,这棵树高度为h. 可是,当curimit把这棵树给陶陶看的时候,陶陶却说:"今年生日不收礼,收礼只收节点数减高度不超过k的苹果树."这下curimit犯难了,curimit送来的树枝繁叶茂,不满足节点数-高度≤k.于是curimit决定剪掉一些枝条,使得修剪过后的树满足节

Android之——多线程下载示例(一)

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/46883927 一.概述 说到Android中的文件下载,Android API中明确要求将耗时的操作放到一个子线程中执行,文件的下载无疑是需要耗费时间的,所以要将文件的下载放到子线程中执行.下面,我们一起来实现一个Android中利用多线程下载文件的小例子. 二.服务端准备 在这个小例子中我以下载有道词典为例,在网上下载有道词典的安装包,在eclipse中新建项目web,将下载的

Swift - 跳跃吃苹果游戏开发(SpriteKit游戏开发)

下面通过一个样例演示如何实现飞行道具的生成,以及道具碰撞拾取. 样例说明: 1,屏幕从右到左不断地生成苹果飞过来(苹果高度随机) 2,点击屏幕可以让熊猫跳跃 3,熊猫碰到苹果,苹果消失 运行效果: 样例代码: 苹果工厂类 AppleFactory.swift 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43

多线程经典面试题

1.基本概念 详见:线程和进程关系和区别.同步和互斥.进程间通信 2.以下多线程对int型变量x的操作,哪几个不需要进行同步(D)        A. x=y;      B. x++;    C. ++x;    D. x=1; 详见:多线程二 多线程中的隐蔽问题揭秘 3.多线程中栈与堆是公有的还是私有的 (C) A:栈公有, 堆私有 B:栈公有,堆公有 C:栈私有, 堆公有 D:栈私有,堆私有 4.临界区(Critical Section)和互斥量(Mutex) 两者都可以用于同一进程中不同