用《捕鱼达人》去理解C#中的多线程_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > 用《捕鱼达人》去理解C#中的多线程

用《捕鱼达人》去理解C#中的多线程

 2014/4/22 3:18:42  黑树  博客园  我要评论(0)
  • 摘要:线程是进程中某个单一顺序的控制流,是程序运行中的调度单位,是程序执行流的最小单位,一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。线程也有就绪、阻塞和运行三种基本状态。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序进程本身。CLR中有三种常用创建和管理线程的方式:Thread、ThreadPool、Task
  • 标签:C# 多线程 理解 线程

线程是进程中某个单一顺序的控制流,是程序运行中的调度单位,是程序执行流的最小单位,一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。 线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。 线程也有就绪、阻塞和运行三种基本状态。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序进程本身。

CLR中有三种常用创建和管理线程的方式:Thread、ThreadPool、Task,下面用最简单的例子写出自己对这三种方式理解

一、Thread

《捕鱼达人》是大家都玩过的游戏,至于游戏怎么设计我也不太清楚,但我想在这里用自己对线程的理解来用线程描述这个游戏。假如屏幕上随机产生两条鱼,并且游来游去,代码如下:
 1       class Fish
 2       {
 3             public string Name { get; set; }
 4  
 5             public Fish()
 6             {
 7                   Name = "小黄鱼" ;
 8             }
 9  
10             public void Move()
11             {
12                   Console.WriteLine(string .Format("{0}在游来游去......", Name));
13             }
14       }
15  
16       class Program
17       {
18             static void Main(string[] args)
19             {
20                   Fish fish = new Fish();
21                   Thread t1 = new Thread(() =>
22                   {
23                         fish.Move();
24                   });
25                   t1.IsBackground = true;
26                   t1.Start();
27  
28                   Fish fish2 = new Fish() { Name = "大鲨鱼" };
29                   Thread t2 = new Thread(() =>
30                   {
31                         fish2.Move();
32                   });
33                   t2.IsBackground = true;
34                   t2.Start();
35  
36                   Console.ReadKey();
37             }
38       }
运行后屏幕如下: 小黄鱼在游来游去...... 大鲨鱼在游来游去...... 

二、ThreadPool

如果鱼潮来临,一下子要产生100条鱼,如果按上面Thread的做法就要开启100条线程,这样对系统资源的损耗太大,这时我们可以用ThreadPool线程池来实现,代码如下:
 1             static void Main(string[] args)
 2             {
 3                    Fish fish = new Fish();
 4                    Fish fish2 = new Fish() { Name = "大鲨鱼" };
 5                    Fish fish3 = new Fish() { Name = "灯笼鱼" };
 6                    Fish fish4 = new Fish() { Name = "红鲤鱼" };
 7                    Fish fish100 = new Fish() { Name = "灯笼鱼" };
 8                    ThreadPool.QueueUserWorkItem(f => { fish.Move(); });
 9                    ThreadPool.QueueUserWorkItem(f => { fish2.Move(); });
10                    ThreadPool.QueueUserWorkItem(f => { fish3.Move(); });
11                    ThreadPool.QueueUserWorkItem(f => { fish4.Move(); });
12                    ThreadPool.QueueUserWorkItem(f => { fish100.Move(); });
13                    Console.ReadKey();
14             }

运行后屏幕如下:

灯笼鱼在游来游去...... 大鲨鱼在游来游去...... 灯笼鱼在游来游去...... 小黄鱼在游来游去...... 红鲤鱼在游来游去...... 由于多线程是并发执行,由系统分配顺序,所以上面的结果是随机的 

三、Task

Task是.Net4.0中新加的功能,由于ThreadPool对池中的线程不好控制,Task用来弥补,比如在鱼在流动的时候,我开了一个枪和炮的线程用来发射子弹捕鱼,鱼中枪后鱼游动的线程就要结束,结束的时候弹出奖励积分,比如小黄鱼弹出1分,大鲨鱼弹出100分,这是就要用到Task对象的ContinueWith方法,该方法可以在线程结束的时候产生一个回调方法,代码如下:  
  1     class Program
  2       {
  3             static void Main(string[] args)
  4             {
  5                   //用来取消小黄鱼线程
  6                   CancellationTokenSource cts = new CancellationTokenSource ();
  7  
  8                   Fish fish = new Fish();
  9                   Fish fish2 = new Fish() { Name = "大鲨鱼" , Score =100 };
 10  
 11                   Task t1 = new Task(() => fish.Move(cts.Token), cts.Token);
 12                   t1.Start();
 13                   //小黄鱼被击中后显示积分
 14                   t1.ContinueWith(fish.ShowScore);
 15  
 16                   Task t2 = new Task(() =>fish2.Move(cts.Token), cts.Token);             
 17                   t2.Start();
 18                   //大鲨鱼鱼被击中后显示积分
 19                   t2.ContinueWith(fish2.ShowScore);
 20  
 21                   //按任意键发射
 22                   Console.ReadKey();
 23  
 24                   //武器工厂线程池,执行一组任务
 25                   Gun gun = new Gun();
 26                   LaserGun laserGun = new LaserGun();
 27                   TaskFactory taskfactory = new TaskFactory();
 28                   Task[] tasks = new Task[]
 29                   {
 30                         taskfactory.StartNew(()=>gun.Fire()),
 31                         taskfactory.StartNew(()=>laserGun.Fire())
 32                   };
 33                   //执行武器们开火
 34                   taskfactory.ContinueWhenAll(tasks, (Task) => { });
 35  
 36                   //鱼儿们被击中了就会去调显示积分的方法
 37                   cts.Cancel();
 38                   Console.ReadLine();
 39             }
 40       }
 41  
 42       class Fish
 43       {
 44             public string Name { get; set; }
 45             public int Score { get; set; }
 46  
 47             public Fish()
 48             {
 49                   Name = "小黄鱼" ;
 50                   Score = 1;
 51             }
 52  
 53             /// <summary>
 54             /// 游动
 55             /// </summary>
 56             public void Move(CancellationToken ct)
 57             {
 58                   //如果没有被击中,就一直游阿游,用IsCancellationRequested判断
 59                   while (!ct.IsCancellationRequested)
 60                   {
 61                          Console.WriteLine(string .Format("{0}在游来游去......", Name));
 62                          Thread.Sleep(1000);
 63                   }                 
 64             }
 65  
 66             //中枪死亡后显示奖励
 67             public void ShowScore(Task task)
 68             {
 69                    Console.WriteLine(string .Format("{0}中弹了,您得到{1}分......" , Name, Score));
 70             }
 71       }
 72  
 73       abstract class Weapon
 74       {
 75              public string Name { get; set; }
 76              public abstract void Fire();
 77       }
 78  
 79       class Gun : Weapon
 80       {
 81             public Gun()
 82                   : base()
 83             {
 84                   Name = "双射枪" ;
 85             }
 86             public override void Fire()
 87             {
 88                   Console.WriteLine(string .Format("咻咻咻,{0}向鱼儿们发射子弹......" , Name));
 89             }
 90       }
 91  
 92       class LaserGun : Weapon
 93       {
 94             public LaserGun()
 95                   : base()
 96             {
 97                   Name = "激光炮" ;
 98             }
 99             public override void Fire()
100             {
101                   Console.WriteLine(string .Format("嗖嗖嗖,{0}向鱼儿们发射炮弹......" , Name));
102             }
103       }   
运行后屏幕如下: 大鲨鱼在游来游去...... 小黄鱼在游来游去...... 大鲨鱼在游来游去...... 小黄鱼在游来游去...... 大鲨鱼在游来游去...... 小黄鱼在游来游去......  按任意键开火后屏幕显示: 大鲨鱼在游来游去...... 小黄鱼在游来游去...... 大鲨鱼在游来游去...... 小黄鱼在游来游去...... 大鲨鱼在游来游去...... 小黄鱼在游来游去...... 咻咻咻,双射枪向鱼儿们发射子弹...... 嗖嗖嗖,激光炮向鱼儿们发射子弹...... 大鲨鱼中弹了,您得到100分...... 小黄鱼中弹了,您得到1分...... 

总结:

本人技术一般,脑子愚钝,很多复杂的资料看不太懂,所以喜欢用简单的例子帮助自己理解某个知识点,上面的例子有很多的不足之处,大神们要看到不要笑话就行。 用到线程的时候,如果数量少就用Thread,但Thread不太好结束,这时可以考虑用CancellationTokenSource 类的Cancel方法,当要用到很多线程的时候就用线程池ThreadPool,当要用到很多线程且要对对应的线程进行控制和回调的时候就用Task。 有关Thread、ThreadPool、Task的详细资料大家可以看官方的资料。 如果本文帮到了你或者让你轻轻地笑了下,就帮点个推荐吧。 谢谢阅读。   
发表评论
用户名: 匿名