[转载]用《捕鱼达人》去理解C#中的多线程 - 黑树 - 博客园

[转载]用《捕鱼达人》去理解C#中的多线程 – 黑树 – 博客园.

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

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

一、Thread

《捕鱼达人》是大家都玩过的游戏,至于游戏怎么设计我也不太清楚,但我想在这里用自己对线程的理解来用线程描述这个游戏。假如屏幕上随机产生两条鱼,并且游来游去,代码如下:
class Fish
      {
            public string Name { get; set; }
 
            public Fish()
            {
                  Name = "小黄鱼" ;
            }
 
            public void Move()
            {
                  Console.WriteLine(string .Format("{0}在游来游去......", Name));
            }
      }
 
      class Program
      {
            static void Main(string[] args)
            {
                  Fish fish = new Fish();
                  Thread t1 = new Thread(() =>
                  {
                        fish.Move();
                  });
                  t1.IsBackground = true;
                  t1.Start();
 
                  Fish fish2 = new Fish() { Name = "大鲨鱼" };
                  Thread t2 = new Thread(() =>
                  {
                        fish2.Move();
                  });
                  t2.IsBackground = true;
                  t2.Start();
 
                  Console.ReadKey();
            }
      }

运行后屏幕如下:
小黄鱼在游来游去……
大鲨鱼在游来游去……
二、ThreadPool
如果鱼潮来临,一下子要产生100条鱼,如果按上面Thread的做法就要开启100条线程,这样对系统资源的损耗太大,这时我们可以用ThreadPool线程池来实现,代码如下:

static void Main(string[] args)
            {
                   Fish fish = new Fish();
                   Fish fish2 = new Fish() { Name = "大鲨鱼" };
                   Fish fish3 = new Fish() { Name = "灯笼鱼" };
                   Fish fish4 = new Fish() { Name = "红鲤鱼" };
                   Fish fish100 = new Fish() { Name = "灯笼鱼" };
                   ThreadPool.QueueUserWorkItem(f => { fish.Move(); });
                   ThreadPool.QueueUserWorkItem(f => { fish2.Move(); });
                   ThreadPool.QueueUserWorkItem(f => { fish3.Move(); });
                   ThreadPool.QueueUserWorkItem(f => { fish4.Move(); });
                   ThreadPool.QueueUserWorkItem(f => { fish100.Move(); });
                   Console.ReadKey();
            }

运行后屏幕如下:
灯笼鱼在游来游去……
大鲨鱼在游来游去……
灯笼鱼在游来游去……
小黄鱼在游来游去……
红鲤鱼在游来游去……
由于多线程是并发执行,由系统分配顺序,所以上面的结果是随机的
三、Task
Task是.Net4.0中新加的功能,由于ThreadPool对池中的线程不好控制,Task用来弥补,比如在鱼在流动的时候,我开了一个枪和炮的线程用来发射子弹捕鱼,鱼中枪后鱼游动的线程就要结束,结束的时候弹出奖励积分,比如小黄鱼弹出1分,大鲨鱼弹出100分,这是就要用到Task对象的ContinueWith方法,该方法可以在线程结束的时候产生一个回调方法,代码如下:

class Program
      {
            static void Main(string[] args)
            {
                  //用来取消小黄鱼线程
                  CancellationTokenSource cts = new CancellationTokenSource ();
 
                  Fish fish = new Fish();
                  Fish fish2 = new Fish() { Name = "大鲨鱼" , Score =100 };
 
                  Task t1 = new Task(() => fish.Move(cts.Token), cts.Token);
                  t1.Start();
                  //小黄鱼被击中后显示积分
                  t1.ContinueWith(fish.ShowScore);
 
                  Task t2 = new Task(() =>fish2.Move(cts.Token), cts.Token);             
                  t2.Start();
                  //大鲨鱼鱼被击中后显示积分
                  t2.ContinueWith(fish2.ShowScore);
 
                  //按任意键发射
                  Console.ReadKey();
 
                  //武器工厂线程池,执行一组任务
                  Gun gun = new Gun();
                  LaserGun laserGun = new LaserGun();
                  TaskFactory taskfactory = new TaskFactory();
                  Task[] tasks = new Task[]
                  {
                        taskfactory.StartNew(()=>gun.Fire()),
                        taskfactory.StartNew(()=>laserGun.Fire())
                  };
                  //执行武器们开火
                  taskfactory.ContinueWhenAll(tasks, (Task) => { });
 
                  //鱼儿们被击中了就会去调显示积分的方法
                  cts.Cancel();
                  Console.ReadLine();
            }
      }
 
      class Fish
      {
            public string Name { get; set; }
            public int Score { get; set; }
 
            public Fish()
            {
                  Name = "小黄鱼" ;
                  Score = 1;
            }
 
            /// <summary>
            /// 游动
            /// </summary>
            public void Move(CancellationToken ct)
            {
                  //如果没有被击中,就一直游阿游,用IsCancellationRequested判断
                  while (!ct.IsCancellationRequested)
                  {
                         Console.WriteLine(string .Format("{0}在游来游去......", Name));
                         Thread.Sleep(1000);
                  }                 
            }
 
            //中枪死亡后显示奖励
            public void ShowScore(Task task)
            {
                   Console.WriteLine(string .Format("{0}中弹了,您得到{1}分......" , Name, Score));
            }
      }
 
      abstract class Weapon
      {
             public string Name { get; set; }
             public abstract void Fire();
      }
 
      class Gun : Weapon
      {
            public Gun()
                  : base()
            {
                  Name = "双射枪" ;
            }
            public override void Fire()
            {
                  Console.WriteLine(string .Format("咻咻咻,{0}向鱼儿们发射子弹......" , Name));
            }
      }
 
      class LaserGun : Weapon
      {
            public LaserGun()
                  : base()
            {
                  Name = "激光炮" ;
            }
            public override void Fire()
            {
                  Console.WriteLine(string .Format("嗖嗖嗖,{0}向鱼儿们发射炮弹......" , Name));
            }
      }

运行后屏幕如下:
大鲨鱼在游来游去……
小黄鱼在游来游去……
大鲨鱼在游来游去……
小黄鱼在游来游去……
大鲨鱼在游来游去……
小黄鱼在游来游去……
按任意键开火后屏幕显示:
大鲨鱼在游来游去……
小黄鱼在游来游去……
大鲨鱼在游来游去……
小黄鱼在游来游去……
大鲨鱼在游来游去……
小黄鱼在游来游去……
咻咻咻,双射枪向鱼儿们发射子弹……
嗖嗖嗖,激光炮向鱼儿们发射子弹……
大鲨鱼中弹了,您得到100分……
小黄鱼中弹了,您得到1分……
总结:
本人技术一般,脑子愚钝,很多复杂的资料看不太懂,所以喜欢用简单的例子帮助自己理解某个知识点,上面的例子有很多的不足之处,大神们要看到不要笑话就行。
用到线程的时候,如果数量少就用Thread,但Thread不太好结束,这时可以考虑用CancellationTokenSource 类的Cancel方法,当要用到很多线程的时候就用线程池ThreadPool,当要用到很多线程且要对对应的线程进行控制和回调的时候就用Task。
有关Thread、ThreadPool、Task的详细资料大家可以看官方的资料。
谢谢阅读,望指点和推荐。

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏