C#中的多线程--持续更新系列

  今年第一次旅行结束,虽然是第二次进去藏区,依旧有高原反应,嚓....不过整个旅途感受到前所未有的放松.更有精力面对接下来的工作和学习.进入今天的主题---C#中的多线程

1、感受多线程

因为文章追求是简单易懂,如果您和我一样,是C#初学者,那么在这一段中,请跟着例子,写一次,每个例子都有分析,并且这里的例子是会把多线程涉及的很多问题先引入出来,在后面的阶段,再深入分析~

 C#是支持多线程滴(貌似是废话.)~一个线程有它独立的执行路径,能够与其他的线程“同时”运行,一个C#程式起始于一个单线程,这个线程是被CLR和操作系统自动创建滴。~啰嗦了这么多了,还是用一段简单的代码加以说明

第一个多线程程式
    class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(WriteYes);
            t.Start();
            while (true)
            {
                Console.Write("No");
            }
        }
        static void WriteYes()
        {
            while (true)
            {
                Console.Write("Yes");
            }
        }
    }

程序运行截图

程序分析:在主线程中创建了一个新的线程"t",完成的任务是重复的输出"yes",同时主线程重复的打印"No",在这里并没有控制线程的执行,所以看到的运行结果是随机的

首先,先看一段书本上的:CLR分配每个线程到自己的内存堆栈上,来保证局部变量的分离运行

多线程调用同一个方法,方法中局部变量执行问题
        static void Main(string[] args)
        {
            new Thread(Go).Start();
            Go();
        }
        static void Go()
        {
            for (int cycles = 0; cycles < 5; cycles++)
            {
                Console.Write("??");
            }
        }

程序分析:在此程式中,声明了一个方法Go,在Go这个方法中声明并使用了一个局部变量'cycles',在主程序中,又开启了一个线程(匿名线程)调用方法Go,和在主线程直接调用Go~通过运行结果可以看出,一共输出了10个"?"

 由此,可见变量'cycles'分别在自己的内存堆栈中创建,互不影响!那是不是所有的情况都是如此的呢?答案显然是否定的

线程中引用共用的实例,共享数据问题,还是先看代码,通过代码来分析

多线程共享实例
        #region 多线程共享实例
        bool done;
        static void Main(string[] args)
        {
            Program pp = new Program();//创建了一个Program的实例
            new Thread(pp.Go).Start();
            pp.Go();
        }
        //这里和上面的“线程间局部变量”中的Go方法不一定,这里是一个实例的方法,
        //也就是需要new Program一个实例才可以调用
        void Go()
        {
            if (!done) { done = true; Console.WriteLine("done"); } 
        }
        #endregion

程序分析:在主程序中,声明了一个Program实例pp,在相同的Program实例中,两个线程都调用了方法Go,此时共享了done字段,所以程序运行只输出一个"done",而不是两个

静态字段在多线程中的影响

多线程中的静态字段
        #region 多线程中的静态字段
        static bool done;
        static void Main(string[] args)
        {
            new Thread(Go).Start();
            Go();
        }
        static void Go()
        {
            if (!done) { done = true; Console.WriteLine("done"); } 
        }

        #endregion

程序运行结果:

 看到这里,也许你会想,静态字段和实例字段在多线程中是否没有任何影响,稍等,我们将上面的代码进行简单的修改,将Go方法修改如下

修改后的Go方法
        static void Go()
        {
            if (!done) { Console.WriteLine("done"); done = true; }
        }

在多次执行这个程式,输出done的次数在1和2次之间没规律的切换~

程序分析:从运行的结果上看,这个输出的次数就不受我们程序的控制,问题就是在一个线程在执行if判断的时候,恰好此时另一个线程正在执行输出语句WriteLine语句.当这个线程执行完WriteLine语句,在将done设置为true之前,执行if判断的线程发现done还是为false,所以也会执行WriteLine语句.导致了问题的产生----线程安全问题

线程安全问题

那么如何解决上面遇到的问题呢?抛开程序来说,我们两个人去做一件事,那么为了避免重复,我们可以这样,先获得做事权限的人,先设置一个标志,告诉后面来的人,我现在正在执行任务,请不要进入现场。程序中也是这样的原理,在C#中提供了lock语句来实现(锁的机制)

线程安全
        #region 线程安全
        static bool done;
        static object locker = new object();
        static void Main(string[] args)
        {
            new Thread(Go).Start();
            Go();
            Console.Read();
        }
        static void Go()
        {
            lock (locker)
            {
                if (!done) { Console.WriteLine("done"); done = true; }
            }
        }
        #endregion

我们再试着多次运行程序,会发现只会输出一个done,当两个线程在争夺一个锁的时候(例子中的是locker),一个线程会处于等待或者说是阻止状态知道那个锁变成可用,确保在同一时刻只有一个线程进入了工作区,所以保证只输出了一次done.
在完成上面几个简单的例子之后,我们应该思考,在何时使用多线程~

何时使用多线程

其实这个话题,我想是仁者见仁智者见智了,但核心的思想都是一致的,多线程程序一般被用来在后台处于耗时的任务,主线程保持运行,并且工作线程在后台默默无闻的工作,对于Winform程序来说,如果主线程(UI线程)执行一段耗时操作的代码,键盘和鼠标的操作会变得很迟钝,时间稍微长点,就整个程序就失去了响应。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted on 2012-04-12 07:13  wolfram  阅读(2171)  评论(3编辑  收藏  举报

导航