日期:2014-05-18  浏览次数:20779 次

C#模拟超人和小怪兽赛跑——多线程学习之(二)牛刀小试

    在上一篇博客中对线程有了一个初步的概要框架,从这篇博客开始,从代码层面深入学习C#的多线程编程艺术……


    线程的重点在于线程的控制,这里使用C#创建控制台程序,模拟超人和小怪兽赛跑的故事,涉及到.NET平台,线程的创建、开启和线程控制中锁的使用,算是多线程学习的一道开胃小菜吧大笑


    故事的背景:


    一天,超人见到了小怪兽,说要进行一场跑步比赛,但是小怪兽(20m/s)说:“超人(50m/s)你跑的速度快,你要比我多跑一段路程”。超人想了想,我是超人,我怕谁?于是就答应道:“好吧,那我就让你200米”。就这样,两人一拍即合,于是乎,请来了一位教练。 可是教练比较懒,在比赛开始前,先宣布比赛规则:第一个跑到终点的选手,自己将名字写到终点的黑板上,然后又分别悄悄地跟超人和小怪兽叫到一边,小声的说:“等你跑完的时候通知我一声,然后我宣布比赛结束”。裁判一声枪响,两人开始冲锋,可是超人起初怎么能想到今年是2012,世界末日?!于是就在他跑到一半的时候,去拯救地球,回来后继续接着跑。小怪兽对超人全然不知,只顾低头跑步。但是在这场逐鹿汇中,究竟鹿死谁家……???


    程序模拟:


    分析上面的过程,我们需要创建超人、小怪兽这两个线程,来执行跑步的方法;定义一个静态的白板,用于记录第一个跑到终点的选手的姓名;裁判要等到两个线程都结束的时候才宣布比赛结束(线程的阻塞);超人跑到一半的时候拯救地球和第一个跑到终点的选手去找粉笔,然后记下自己的名字,都是模拟的线程中执行了其他的方法导致的阻塞或延时;系统中定义的白板变量是两个线程公用的数据,在程序中使用锁的方式达到异步读和同步写的目的。

1、定义超人和小怪兽的线程,并声明白板变量,并附带一把锁。

        //定义静态的超人和小怪兽的线程,可以让主线程识别——定义线程
        private static Thread superman;
        private static Thread master;
        //设计一个记录选手名单的白板
        private static string nameboard;
        //定义一个专门用于多线程的锁对象,是一种互斥锁。是引用类型的,引用的是对象的地址,这样不管其他的程序拿到多少个引用,最终还是同一个实际数据;不能是值类型,因为如果是值类型的时候,就是实际数据的副本,不同的对象使用的时候,是对象数据的副本,这样修改后数据就不一致了;引用类型,不管被引用了多少次,但是实例只有一个,一旦这个实例被上锁后,实例只可以被一个人抢占。
        private static object lockobj = new object();

2、程序模拟超人和小怪兽跑步的方法——runnerWork

        /// <summary>
        /// 赛跑的方法。是每一个选手公共工作的方法,返回值是空(Thread定义的委托决定的),传入object表示赛跑的路程(封装的是int类型的参数,必须是object类型,系统已经定义好的委托的类型决定的)
        /// obj封装赛跑的路程,设计一个拆装箱的过程 
        /// </summary>
        /// <param name="obj"></param>
        private static void  runnerWork( object obj )
        {
            int length = Int32.Parse(obj.ToString());

            #region 关于获取线程名的几种写法比较

            //最安全的获取线程名称的写法。但在赋值号中可能会被其他线程打断,这种可能性很小,另外我们还可以配合锁,来解决这个问题
            string currentname = Thread.CurrentThread.Name;

            ////这种写法是安全的,因为线程内部会保留线程的一个副本名称,线程与线程之间是相互隔离的,每一个线程会在内存中开辟自己的一段空间,所以,并不是线程越多越好。
            //Thread  curThread  = Thread.CurrentThread;
            //string strThreadName = curThread.Name;

            ////这种写法是不安全的,因为两行代码之间随时可能会有其他的线程插入
            //Thread curThread = Thread.CurrentThread;
            //string strThreadName = Thread.CurrentThread.Name;

            #endregion

            #region 给超人和小怪兽的速度定义一个数值            
            int speed ;
            if (currentname == superman.Name)
            {
                speed = 50;
            }
            //针对已知的线程使用else if,目的是为了提高线程程序的可扩展性,在多线程中尤为重要
            else if (currentname == master.Name)
            {

                speed = 20;
            }
            else
            {
                speed = 1;
            }
            #endregion

            #region 模拟跑步的过程

            Console.WriteLine("<" + currentname +">开始起跑……\n");
            
            for (int count = speed; count <= length; count += speed)
            {
                Thread.Sleep(1000);
                Console.WriteLine("<" + currentname +">跑到了第"+ count.ToSt