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

来源:互联网 发布:如何查询手机淘宝余额 编辑:程序博客网 时间:2024/05/18 01:31

线程是进程中某个单一顺序的控制流,是程序运行中的调度单位,是程序执行流的最小单位,一个标准的线程由线程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 Program17       {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的详细资料大家可以看官方的资料。
如果本文帮到了你或者让你轻轻地笑了下,就帮点个推荐吧。
谢谢阅读。
0 0
原创粉丝点击