漫谈多线程(上)_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > 漫谈多线程(上)

漫谈多线程(上)

 2014/8/22 16:24:50  VitoCorleone  程序员俱乐部  我要评论(0)
  • 摘要:hey,youguys.    好久不见了,最近忙着学习英文,处理一些杂事,所以没有来得及更新博客。公司目前没活,比较清闲。所以,有时间研究了一下<叩响C#之门>。据说作者是一位40多岁的初中数学老师,自学C#。40多岁的人自学编程,这份毅力很令人敬佩。这本书写的,是C#语言的基础知识。作者讲解的很清楚,读后很受益。很多之前一知半解,甚至一点都不懂得基础原理,现在豁然开朗。就像孔子所说:“朝闻道,夕死可矣。”这本书,真的适合初级
  • 标签:

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" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="QQ Photo20140822112205" src="/Upload/Images/2014082216/11B056B20484FC7E.jpg" width="644" height="414" />

我们通过效果图看以看出,两个线程基本是平均交替执行的,看不出优先级的高低。因为,默认的线程优先级是一般(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。

  • 相关文章
发表评论
用户名: 匿名