• 漫谈多线程(上)


    hey,you guys.

         好久不见了,最近忙着学习英文,处理一些杂事,所以没有来得及更新博客。公司目前没活,比较清闲。所以,有时间研究了一下<叩响C#之门>。据说作者是一位40多岁的初中数学老师,自学C#。40多岁的人自学编程,这份毅力很令人敬佩。这本书写的,是C#语言的基础知识。作者讲解的很清楚,读后很受益。很多之前一知半解,甚至一点都不懂得基础原理,现在豁然开朗。就像孔子所说:“朝闻道,夕死可矣。”这本书,真的适合初级.NET程序员读一下,真的很有帮助的。好了,书归正传,来开始我们今天的学习吧。

       多线程这块,在.NET中属于深层次的技术了。今天,我们就来学习学习多线程的知识吧。

    线程的概念

    大家平时使用电脑,可以一边听音乐,一边下载电影,一边浏览网页。那么电脑它是如何满足用户这种需求呢?原来操作系会创建三个应用程序,通过应用程序来执行用户的操作。每个应用程序被看作一条连续的指令流,CPU一条条执行这些指令流。但是早一些的电脑CPU不允许同一时间执行多条指令流,那么电脑又是如何做的呢?操作系统创建三个应用程序的同时,也创建了三个进程(Process)。进程不但包括了应用程序的指令流,还包括了运行应用程序所需的内存、寄存器等等。操作系统通过进程来执行应用程序。我们可以把进程看作是执行路线。操作系统 通过”时间片轮转”来轮流执行这些进程。所以,宏观上进程是并发执行的,在微观上是交替进行的。也就是说,电脑并不是同时满足听音乐、下载电影、浏览网页的,CPU是交替执行的,只不过交替时间很短暂,我们感觉不到。我们设想这样一个场景,一个网页播放Flash的同时,网页上还有一个文本框,来接受用户的输入。这个网页需要一边播放Flash,一边接受用户的输入。Note (个人理解,CPU执行这个网页的进程指令,同时这个进程还要执行播放Flash、接受用户输入这两个指令)。如果是以前,我们需要很复杂的一段代码来实现这个需求。但是多线程(Multi-Threading)技术的出现,就可以很容易的实现这个需求了。可以在网页这个进程中创建两个线程,通过这两个线程来执行播放Flash、接受用户输入。我们可以把进程中的多个线程,看作是多条执行路线。一个进程中的多个线程,它们共享资源。所以,线程之间的切换要远远快于进程之间的切换,我们可以把线程看作是轻量级的进程(LightweightProcess)。操作系统通过调度程序来管理线程,不需程序员关心,我们只需编写好线程即可,线程是CPU调度的基本单位。

    Thread类

    计算机运行某个应用程序时,会创建一个进程,进程会创建多个线程,通过线程来执行工作,我们把执行某些工作的线程称为工作线程(Work Thread)。C#中,关于线程的处理,都是通过System.Threading命名空间下的Thread类来完成的。

    Using System.Threading;   //别忘了添加引用
    
    //声明一个线程的代码
    Thread workThread=new Thread(entryPoint);

    entryPoint:是入口方法,线程会从入口方法的第一行代码执行。大家应该可以看出,Thread类的构造函数的参数类型是一个委托类型。也就是说entryPoint的函数的返回值、参数取决于Thread类的构造函数的委托参数决定。

    //Thread类的构造函数参数的委托类型
    public delegate void ThreadStart();
    
    public delegate void ParameterizedThreadStart(object obj);

    入口方法,必须是ThreadStart或ParameterizedThreadStart的委托。

    那么综上所述,如果要创建一个线程,需要两步.

    //第一步 创建入口方法
    private void EntryPoint()
    {
      //线程的具体代码
       ...........
        ...........
    }
    
    //第二步 创建线程对象
    Thread workThread=new Thread(EntryPoint);

    线程的优先级

    我们工作生活中,需要处理很多事情。一般当紧的事儿需要及时处理,不当紧的事儿可放在后面处理。线程处理指令也是一样的,也分当紧与不当紧。通过Thread类的Priority属性来设置线程的优先级。Priority属性是一个ThreadPriority枚举。这个枚举有以下5个优先等级:

    Normal、AboveNormal、Highest、BelowNormal、Lowest。

    我们跑一下程序来测试一下线程优先级:

                //workThread1线程
                Thread workThread1 = new Thread(delegate()
                {
                    for (int i = 0; i < 100000000; i++)
                    {
                        if (i % 1000000 == 0)
                        {
                            Console.Write("A");
                        }
                    }
                });
    
                //workThread2线程
                Thread workThread2 = new Thread(delegate()
                {
                    for (int i = 0; i < 100000000; i++)
                    {
                        if (i % 1000000 == 0)
                        {
                            Console.Write("B");
                        }
                    }
                });
    
                //启动线程
                workThread1.Start();
                workThread2.Start();

    运行程序,效果如下图:

    QQ Photo20140822112205

    我们通过效果图看以看出,两个线程基本是平均交替执行的,看不出优先级的高低。因为,默认的线程优先级是一般(Normal)级别的。下面,我们修改一下程序,改变线程的优先级:

                 //workThread1线程
                Thread workThread1 = new Thread(delegate()
                {
                    for (int i = 0; i <= 500000000; i++)
                    {
                        if (i % 1000000 == 0)
                        {
                            Console.Write('A');
                        }
                    }
                });
    
                //workThread2线程
                Thread workThread2 = new Thread(delegate()
                {
                    for (int i = 0; i <= 500000000; i++)
                    {
                        if (i % 1000000 == 0)
                        {
                            Console.Write('B');
                        }
                    }
                });
    
                //修改Thread的优先级
                workThread1.Priority = ThreadPriority.AboveNormal;
                workThread2.Priority = ThreadPriority.BelowNormal;
                //启动线程
                workThread1.Start();
                workThread2.Start();
    
                //主线程代码
                for (int i = 0; i <= 500000000; i++)
                {
    
                    if (i % 1000000 == 0)
                    {
                        Console.Write('M');
                    }
                }

    其实除了workThread1、workThread2还有一个主线程(Main Thread)。我们修改了一下主线程的代码,以便观察三个线程是如何交叉工作的。我们代码中把workThread的Priority的属性设置为高于一般(AboveNormal)、把workThread2的Priority属性设置为低于一般(BelowNormal)。此时运行程序的效果如下图:

    QQ Photo20140822135626

    Note that:优先级高,只能证明占用CPU的时间长,并不代表,只有执行完优先级高的线程,才会执行优先级低的线程。在图中,我们也可以看出,B也回出现在A之前的。

    线程插入

    我们可以通过Thread类的Join()方法,将两个原本交替执行的线程,变为顺序执行。我们通过代码来观察这个Join()方法:

              static void Main(string[] args)
                 {
                
                //workThread1线程
                Thread workThread1 = new Thread(delegate()
                {
                    for (int i = 0; i <= 500000000; i++)
                    {
                        if (i % 1000000 == 0)
                        {
                            Console.Write('A');
                        }
                    }
                });
    
                //workThread2线程
                Thread workThread2 = new Thread(delegate()
                {
                    for (int i = 0; i <= 50000000; i++)
                    {
                        if (i % 1000000 == 0)
                        {
                            Console.Write('B');
                        }
                    }
                    workThread1.Join();
                    for (int i = 0; i <= 50000000; i++)
                    {
                        if (i % 10000 == 0)
                        {
                            Console.Write('b');
                        }
                        
                    }
                });
    
                
                //启动线程
                  workThread1.Start();
                workThread2.Start();
    
          
            }

       我们在线程workThread2的入口方法中,调用了workThread1的join()方法,此时当程序执行到join()方法时,workThread2就会停止工作,去执行workThread1,直到workThread1执行完毕,才会接着执行workThread2。程序运行结果如下图:

    QQ Photo20140822141249

    Join()方法,还可以接受一个表示毫秒的参数,当达到这个时间,不论是否执行完毕,都会退出。

    线程状态

    线程一共有7种状态,它们分别是未开始状态(UnStarted)、运行状态(Running)、等待睡眠插入状态(WaitSleepJoin)、挂起请求状态(SuspendRequested)、挂起状态(Suspended)、中止请求状态(AbortRequested)、中止状态(Stopped)。大家可以通过下面这幅图片来认识这7中状态:

    QQ Photo20140822142019

    1.未开始状态(Unstarted)

    当一个线程被创建,它的状态会变为未开始状态(UnStarted).

    2.运行状态(Running)

    当线程调用Start()方法时,线程状态就会变为运行状态(Ruuning)。

    3.等待睡眠插入状态(WaitSleepJoin)

    当运行状态的线程调用方法Sleep()、Join()、或Wait()时,线程状态会变为等待睡眠插入状态(WaitSleepJoin).此时如果调用Pulse()或Interrupt()线程状态会变为Running状态。

    4.挂起请求状体(SuspendRequested)

    当运行状态的线程调用方法Suspend()的时,线程状态会变为SuspendRequested。

    5.挂起状态(Suspended)

    当调用Suspend()方法时,线程不会立马被挂起,而是会处于SuspendRequested状态,线程会再执行几条指令,当确保线程在一个安全的状态下,挂起线程,线程状态变为Suspended。调用Resume()方法可以使线程状体变为Running状态。

    6.中止请求状态(AbortRequested)

    当处于运行状态的线程调用方法Abort()时,线程状态会变为AbortRequested。

    7.中止状态(Aborted)

    当线程调用Abort()方法时,线程不会马上中止,而会处于AbortRequested状态,再执行几条指令,当确保线程在一个安全状态下,中止线程,线程状态变为Stopped。

  • 相关阅读:
    深究递归和迭代的区别、联系、优缺点及实例对比
    提高Python运行效率的六个窍门
    C++设计模式——单例模式
    使用Python的turtle库实现七段数码管绘制
    Python 死循环和嵌套循环
    Python 随机数 random
    更改 pandas dataframe 中两列的位置
    Pandas中DataFrame修改列名
    MM 算法与 EM算法概述
    机器学习中的训练集、验证集和测试集
  • 原文地址:https://www.cnblogs.com/VitoCorleone/p/3929490.html
Copyright © 2020-2023  润新知