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