你的位置:首页 > ASP.net教程

[ASP.net教程][.net面向对象程序设计进阶] 多线程 (Multithreading) 利用多线程提高程序性能


[.net面向对象程序设计进阶](16) 多线程 (Multithreading) 利用多线程提高程序性能

本节导读:

随着硬件和网络的高速发展,为多线程(Multithreading)处理并行任务,提供了有利条件。

其实我们每时每刻都在享受多线程带来的便利,多核处理器多线程工作、Windows操作系统、Web服务器都在使用多线程工作。

使用多线程直接提高了程序的执行效率,因此学习多线程对提高程序运行能力非常必要,本节主要介绍多线程原理及.NET中多线程在.NET面向对象程序设计中的应用。

1. 关于多线程

在介绍多线程之前,先了解一下进程。

进程:独立运行的程序称为进程。(比如Windows系统后台程序,也可以称为后台进程)

线程:对于同一个程序分为多个执行流,称为线程。

多线程:使用多个线程进行多任务处理,称为多线程。

2. 如何合理使用多线程?

A.对于用户等待程序处理时,可以使用多线程处理耗时任务;

B.对于一些不需要即时完成的任务,可以使用后台任务线程处理;

C.对于多并发任务,可以使用多线程同时处理;

D.对于通讯类,比如对线程阻塞,可以使用多线程。

除过上面的几个常用的情况,还有很多情况下可以使用多线程。

3. 多线程的缺点
线程自然也有缺点,以下列出了一些:
A.如果有大量的线程,会影响性能,因为操作系统需要在他们之间切换;
B.更多的线程需要更多的内存空间;
C.线程会给程序带来更多的bug,因此要小心使用,比如:线程任务在执行完成后,要及时释放内存;
D.线程的中止需要考虑其对程序运行的影响。

4. .NET中的两种多线程

.NET本身就是一个多线程的的环境。

在.NET中有两种多线程的:

一种是使用Thread类进行线程的创建、启动,终止等操作。

一种是使用ThreadPool类用于管理线程池.

5 .NET中使用Thread进行多线程处理

5.1 Thread类常用方法

.NET基础类库的System.Threading命名空间提供了大量的类和接口支持多线程。System.Threading.Thread类是创建并控制线程,设置其优先级并获取其状态最为常用的类。

下面是该类几个至关重要的方法:
Thread.Start():启动线程的执行;
Thread.Suspend():挂起线程,或者如果线程已挂起,则不起作用;
Thread.Resume():继续已挂起的线程;
Thread.Interrupt():中止处于Wait或者Sleep或者Join线程状态的线程;
Thread.Join():阻塞调用线程,直到某个线程终止时为止
Thread.Sleep():将当前线程阻塞指定的毫秒数;
Thread.Abort():以开始终止此线程的过程。如果线程已经在终止,则不能通过Thread.Start()来启动线程。

下面是一个简单的线程示例: 

先创建一个方法:

//简单线程方法static void MyThreadStart(){  Console.WriteLine("我是一个简单线程");}

创建线程调用方法:

//简单的线程Thread myThread = new Thread(MyThreadStart);myThread.Start();

运行结果如下:

5.2 Thread类常用属性

Thread的属性有很多,我们先看最常用的几个:

CurrentThread :用于获取当前线程; 

ThreadState 当前线程的状态(5.4介绍);

Name:获取或设置线程名称;

Priority:获取或设置线程的优先级(5.5介绍)

ManagedThreadId:获取当前线程的唯一标识

IsBackground:获取或设置线程是前台线程还是后台线程(5.6介绍)

IsThreadPoolThread:获取当前线程是否是托管线程池(后面章节会介绍)

下面创建一个线程示例,来说明这几个属性: 

Thread myThreadTest = new Thread(() =>{  Thread.Sleep(1000);  Thread t = Thread.CurrentThread;  Console.WriteLine("Name: " + t.Name);  Console.WriteLine("ManagedThreadId: " + t.ManagedThreadId);  Console.WriteLine("State: " + t.ThreadState);  Console.WriteLine("Priority: " + t.Priority);  Console.WriteLine("IsBackground: " + t.IsBackground);  Console.WriteLine("IsThreadPoolThread: " + t.IsThreadPoolThread);}){  Name = "线程测试",  Priority = ThreadPriority.Highest};myThreadTest.Start();Console.WriteLine("关联进程的运行的线程数量:"+System.Diagnostics.Process.GetCurrentProcess().Threads.Count);

运行结果如下:

  

5.3 带参数的线程方法 

首先我们使用Lambda表达式来改写前面“简单线程”中无参数的方法,如下: 

//线程一new Thread(()=>{  for (int i = 0; i < 5; i++)    Console.WriteLine("我的线程一-[{0}]", i);}).Start();

 运行结果如下:

上面示例创建的线程并没有带参数,如果是一个有参数的方法,线程该如何创建?

别担心,.NET为我们提供了一个ParameterizedThreadStart委托来解决带一个参数的问题,如下:

//线程二new Thread((num) =>{  for (int i = 0; i < (int)num; i++)    Console.WriteLine("我的线程二--[{0}]", i);}).Start(5);

运行结果如下:

那么问题来了,ParameterizedThreadStart委托只有一个包含数据的参数,对于多个参数呢?我们可以使用一个无参数的方法来包装它,如下:

先创建一个带参数的方法:

static void myThreadStart(int numA, int numB){  for (int i = (int)numA; i < (int)numB; i++)    Console.WriteLine("我的线程三---[{0}]", i);}

然后通过无参数的委托来包装它,如下:

//线程三new Thread(() =>myThreadStart(0,5)).Start();

运行结果如下:

 

5.4  Thread状态

我们对于线程启动以后,如何进行挂起和终止、重新启用,首先线程在运行后有一个状态。

System.Threading.Thread.ThreadState属性定义了执行时线程的状态。线程从创建到线程终止,它一定处于其中某一个状态。

A.Unstarted:当线程被创建时,它处在Unstarted状态。

B.Running:Thread类的Start() 方法将使线程状态变为Running状态,线程将一直处于这样的状态,除非我们调用了相应的方法使其挂起、阻塞、销毁或者自然终止。

C.Suspended:如果线程被挂起,它将处于Suspended状态。

D.Running:我们调用resume()方法使其重新执行,这时候线程将重新变为Running状态。一

E.Stopped:旦线程被销毁或者终止,线程处于Stopped状态。处于这个状态的线程将不复存在,正如线程开始启动,线程将不可能回到Unstarted状态。

F.Background:线程还有一个Background状态,它表明线程运行在前台还是后台。在一个确定的时间,线程可能处于多个状态。

G.WaitSleepJoin、AbortRequested:举例子来说,一个线程被调用了Sleep而处于阻塞,而接着另外一个线程调用Abort方法于这个阻塞的线程,这时候线程将同时处于WaitSleepJoin和AbortRequested状态。

H.一旦线程响应转为Sle阻塞或者中止,当销毁时会抛出ThreadAbortException异常。

ThreadState枚举的10种执行状态如下:

 

对于线程阻塞和同步问题,将在下一节继续介绍。

5.5. 线程优先级

对于多线程任务,我们可以根据其重要性和运行所需要的资源情况,设置他的优先级
 System.Threading.ThreadPriority枚举了线程的优先级别,从而决定了线程能够得到多少CPU时间。

高优先级的线程通常会比一般优先级的线程得到更多的CPU时间,如果不止一个高优先级的线程,操作系统将在这些线程之间循环分配CPU时间。

低优先级的线程得到的CPU时间相对较少,当这里没有高优先级的线程,操作系统将挑选下一个低优先级的线程执行。

一旦低优先级的线程在执行时遇到了高优先级的线程,它将让出CPU给高优先级的线程。

新创建的线程优先级为一般优先级,我们可以设置线程的优先级别的值,如下面所示: 

对于线程的优先级我们下面做一个实验,开启两个线程(一个设置高优先级,另一个设置低优先级)。

分别委托两个方法进行累加,看一下最终结果,代码如下:

int numberA = 0, numberB = 0;bool state = true;new Thread(() => { while (state)numberA++; }) { Priority = ThreadPriority.Highest, Name="线程A" }.Start();new Thread(() => { while (state)numberB++; }) { Priority = ThreadPriority.Lowest , Name="线程B" }.Start();//让主线程挂件1秒Thread.Sleep(1000);state = false;Console.WriteLine("线程A: {0}, 线程B: {1}", numberA, numberB);

运行结果如下:

.NET根据优先级分配了资源,可以看到优先级较高的线程执行的机会较大,但优先级小的线程仍然有较多的机会执行。

5.6  前台线程和后台线程

线程有两种,默认情况下为前台线程,要想设置为后台线程也非常容易,只需要加一个属性:thread.IsBackground = true;就可以变为一个后台线程了。

重点来了,前后台线程的区别:

A.前台线程:应用程序必须执行完所有的前台线程才能退出;

B.后台线程:应用程序不必考虑其是否全部完成,可以直接退出。应用程序退出时,自动终止后台线程。

下面我们使用一个输出从0到1000的数字,来实验一下前台线程和后台线程的区别:

 

Thread myThread = new Thread(() =>{for (int i = 0; i < 1000; i++)Console.WriteLine(i);});     var key = Console.ReadLine();if (key == "1"){  myThread.IsBackground = true;  myThread.Start();}else{  myThread.IsBackground = false;  myThread.Start();}

 当我们在控制台等级输入的时候,

如果输入1(后台线程),线程会很快关闭,并不会等输出完1000个数字再关闭;

如果输入其它,回车后,则线程会等1000个数字输出完后,窗口关闭;

6. 本节要点:

A.本节主要介绍了线程的基本知识;

B.Thread常用的属性、方法; 

C.Thread委托的方法有多个参数的用法;

D.Thread的优先级;

E.Thread的执行状态;

F.前台线程和后台线程;

后面会继续深入介绍利用线程提高程序性能。




云南旅游线路报价云南旅游攻略大全云南旅游路线推荐云南自驾游攻略云南旅游线路大全广州塔蜡像馆五一有什么活动?广州塔世界名人蜡像馆五一门票优惠吗? 桂林甲天下广场到神龙水世界怎么走?桂林甲天下广场到神龙水世界有多远? 桂林到神龙水世界在哪坐车?桂林到神龙水世界有直达车吗? 桂林汽车站到神龙水世界有直达车吗?桂林汽车站到神龙水世界坐几路车? 桃花帘外春意暖 江苏求桃花好去处 盘古山禾雀花什么时候开?肇庆盘古山禾雀花开花时间? 春天里带你走进异域风情小镇 感受独特文化[三] 广东3月赏花攻略?广东赏樱花好去处? 湘潭韶山美食有什么? 湖南水府怎么去? 成都美食有那些? 攀枝花有什么美食? 泰国有那些免税店?具体有那些免税商城? 泰国曼谷去哪里购物好呢?曼谷去哪里购物便宜又实惠呢? 一个人去泰国旅游注意什么?尤其是女生要注意什么? 去泰国带点什么东西回来好呢?泰国化妆品便宜吗? IDT5V991A-7JGI Datasheet IDT5V991A-7JGI Datasheet IDT71V3559S75PFGI Datasheet IDT71V3559S75PFGI Datasheet IDT72V3650L7-5PF8 Datasheet IDT72V3650L7-5PF8 Datasheet 大庆跟团港澳五日游 大庆跟团港澳五日游 大庆跟团港澳五日游 黑龙江跟团港澳五日游 黑龙江跟团港澳五日游 黑龙江跟团港澳五日游 牡丹江去港澳3日游 牡丹江去港澳3日游 牡丹江去港澳3日游