zoukankan      html  css  js  c++  java
  • 用《捕鱼达人》去理解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分...... 
     
     
    from:http://www.cnblogs.com/maitian-lf/p/3678128.html#undefined
     
  • 相关阅读:
    ORACLE时间字段取年、月、日、季度【转】
    Oracle查询指定索引提高查询效率【转】
    ORACLE常用命令【转】
    ORACLE中LOB字段的使用和维护
    Oracle建立DBLINK的详细步骤记录【转】
    Oracle实用日期函数总结[转]
    js脚本中过滤特殊字符的正则表达式
    获取上一页面的URL的方法
    Repeat控件绑定数据格式显示
    我刚做的一个TreeView的CheckBox进行选中插入数据库,从数据库中读取数据后让CheckBox勾选的代码!
  • 原文地址:https://www.cnblogs.com/youmingkuang/p/10064688.html
Copyright © 2011-2022 走看看