多线程(.NET)

来源:互联网 发布:mac 安装多个xcode 编辑:程序博客网 时间:2024/06/06 10:39

[编辑本段]
什么是多线程?

  在计算机编程中,一个基本的概念就是同时对多个任务加以控制。许多程序设计问题都要求程序能够停下手

  头的工作,改为处理其他一些问题,再返回主进程。可以通过多种途径达到这个目的。最开始的时候,那些

  拥有机器低级知识的程序员编写一些“中断服务例程”,主进程的暂停是通过硬件级的中断实现的。尽管这

  是一种有用的方法,但编出的程序很难移植,由此造成了另一类的代价高昂问题。

  有些时候,中断对那些实时性很强的任务来说是很有必要的。但还存在其他许多问题,它们只要求将问题划

  分进入独立运行的程序片断中,使整个程序能更迅速地响应用户的请求。在一个程序中,这些独立运行的片

  断叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。多线程处理一个常见的例子就是用

  户界面。利用线程,用户可按下一个按钮,然后程序会立即作出响应,而不是让用户等待程序完成了当前任

  务以后才开始响应。

  最开始,线程只是用于分配单个处理器的处理时间的一种工具。但假如操作系统本身支持多个处理器,那么

  每个线程都可分配给一个不同的处理器,真正进入“并行运算”状态。从程序设计语言的角度看,多线程操

  作最有价值的特性之一就是程序员不必关心到底使用了多少个处理器。程序在逻辑意义上被分割为数个线

  程;假如机器本身安装了多个处理器,那么程序会运行得更快,毋需作出任何特殊的调校。

  根据前面的论述,大家可能感觉线程处理非常简单。但必须注意一个问题:共享资源!如果有多个线程同时

  运行,而且它们试图访问相同的资源,就会遇到一个问题。举个例子来说,两个进程不能将信息同时发送给

  一台打印机。为解决这个问题,对那些可共享的资源来说(比如打印机),它们在使用期间必须进入锁定状

  态。所以一个线程可将资源锁定,在完成了它的任务后,再解开(释放)这个锁,使其他线程可以接着使用

  同样的资源。

  多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。

  使用线程的好处有以下几点:

  ·使用线程可以把占据长时间的程序中的任务放到后台去处理

  ·用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度

  ·程序的运行速度可能加快

  ·在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下可以释放一些珍贵的资源如内存占用等等。

  还有其他很多使用多线程的好处,这里就不一一说明了。

  一些线程模型的背景

  可以重点讨论一下在Win32环境中常用的一些模型。

  ·单线程模型

  在这种线程模型中,一个进程中只能有一个线程,剩下的进程必须等待当前的线程执行完。这种模型的缺点在于系统完成一个很小的任务都必须占用很长的时间。

  ·块线程模型(单线程多块模型STA)

  这种模型里,一个程序里可能会包含多个执行的线程。在这里,每个线程被分为进程里一个单独的块。每个进程可以含有多个块,可以共享多个块中的数据。程序规定了每个块中线程的执行时间。所有的请求通过Windows消息队列进行串行化,这样保证了每个时刻只能访问一个块,因而只有一个单独的进程可以在某一个时刻得到执行。这种模型比单线程模型的好处在于,可以响应同一时刻的多个用户请求的任务而不只是单个用户请求。但它的性能还不是很好,因为它使用了串行化的线程模型,任务是一个接一个得到执行的。

  ·多线程块模型(自由线程块模型)

  多线程块模型(MTA)在每个进程里只有一个块而不是多个块。这单个块控制着多个线程而不是单个线程。这里不需要消息队列,因为所有的线程都是相同的块的一个部分,并且可以共享。这样的程序比单线程模型和STA的执行速度都要块,因为降低了系统的负载,因而可以优化来减少系统idle的时间。这些应用程序一般比较复杂,因为程序员必须提供线程同步以保证线程不会并发的请求相同的资源,因而导致竞争情况的发生。这里有必要提供一个锁机制。但是这样也许会导致系统死锁的发生。

  进程和线程都是操作系统的概念。进程是应用程序的执行实例,每个进程是由私有的虚拟地址空间、代码、数据和其它各种系统资源组成,进程在运行过程中创建的资源随着进程的终止而被销毁,所使用的系统资源在进程终止时被释放或关闭。

  线程是进程内部的一个执行单元。系统创建好进程后,实际上就启动执行了该进程的主执行线程,主执行线程以函数地址形式,比如说main或WinMain函数,将程序的启动点提供给Windows系统。主执行线程终止了,进程也就随之终止。

  每一个进程至少有一个主执行线程,它无需由用户去主动创建,是由系统自动创建的。用户根据需要在应用程序中创建其它线程,多个线程并发地运行于同一个进程中。一个进程中的所有线程都在该进程的虚拟地址空间中,共同使用这些虚拟地址空间、全局变量和系统资源,所以线程间的通讯非常方便,多线程技术的应用也较为广泛。多线程可以实现并行处理,避免了某项任务长时间占用CPU时间。要说明的一点是,目前大多数的计算机都是单处理器(CPU)的,为了运行所有这些线程,操作系统为每个独立线程安排一些CPU时间,操作系统以轮换方式向线程提供时间片,这就给人一种假象,好象这些线程都在同时运行。由此可见,如果两个非常活跃的线程为了抢夺对CPU的控制权,在线程切换时会消耗很多的CPU资源,反而会降低系统的性能。这一点在多线程编程时应该注意。Win32 SDK函数支持进行多线程的程序设计,并提供了操作系统原理中的各种同步、互斥和临界区等操作。Visual C++ 6.0中,使用MFC类库也实现了多线程的程序设计,使得多线程编程更加方便。[6]

[编辑本段]
多线程在.NET里如何工作?

  在本质上和结构来说,.NET是一个多线程的环境。有两种主要的多线程方法是.NET所提倡的:使用ThreadStart来开始你自己的进程,直接的(使用ThreadPool.QueueUserWorkItem)或者间接的(比如Stream.BeginRead,或者调用BeginInvoke)使用ThreadPool类。一般来说,你可以"手动"为长时间运行的任务创建一个新的线程,另外对于短时间运行的任务尤其是经常需要开始的那些,进程池是一个非常好的选择。进程池可以同时运行多个任务,还可以使用框架类。对于资源紧缺需要进行同步的情况来说,它可以限制某一时刻只允许一个线程访问资源。这种情况可以视为给线程实现了锁机制。线程的基类是System.Threading。所有线程通过CLI来进行管理。

  ·创建线程:

  创建一个新的Thread对象的实例。Thread的构造函数接受一个参数:

  Thread DummyThread = new Thread( new ThreadStart(dummyFunction) );

  ·执行线程:

  使用Threading命名空间里的start方法来运行线程:

  DummyThread.Start ();

  ·组合线程:

  经常会出现需要组合多个线程的情况,就是当某个线程需要其他线程的结束来完成自己的任务。假设DummyThread必须等待DummyPriorityThread来完成自己的任务,只需要这样做:

  DummyPriorityThread.Join() ;

  ·暂停线程:

  使得线程暂停给定的秒

  DummyPriorityThread.Sleep(<Time in Second>);

  ·中止线程:

  如果需要中止线程可以使用如下的代码:

  DummyPriorityThread.Abort();

  ·同步

  经常会遇到需要在线程间进行同步的情况,下面的代码给出了一些方法:

  using System;

  using System.Threading;

  namespace SynchronizationThreadsExample

  {

  class SynchronizationThreadsExample

  {

  private int counter = 0;

  static void Main( )

  {

  SynchronizationThreadsExample STE = new SynchronizationThreadsExample();

  STE.ThreadFunction( );

  }

  public void ThreadFunction ( )

  {

  Thread DummyThread = new Thread( new ThreadStart(SomeFunction)) ;

  DummyThread.IsBackground=true;

  DummyThread.Name = "First Thread";

  DummyThread.Start( );

  Console.WriteLine("Started thread ", DummyThread.Name);

  Thread DummyPriorityThread = new Thread( new ThreadStart(SomeFunction) );

  DummyPriorityThread.IsBackground=true;

  DummyPriorityThread.Name = "Second Thread";

  DummyPriorityThread.Start( );

  Console.WriteLine("Started thread ", DummyPriorityThread.Name);

  DummyThread.Join( );

  DummyPriorityThread.Join( );

  }

  public void SomeFunction( )

  {

  try

  {

  while (counter < 10)

  {

  int tempCounter = counter;

  tempCounter ++;

  Thread.Sleep(1);

  counter = tempCounter;

  Console.WriteLine( "Thread . SomeFunction: "+Thread.CurrentThread.Name+counter);

  }

  }

  catch (ThreadInterruptedException Ex)

  {

  Console.WriteLine( "Exception in thread "+Thread.CurrentThread.Name);

  }

  finally

  {

  Console.WriteLine( "Thread Exiting. "+Thread.CurrentThread.Name);

  }

  }

  }

  }

  ·使用Interlock

  C#提供了一个特殊的类叫做interlocked,就是提供了锁机制的实现,可以加入如下的代码实现锁机制:

  Interlocked.SomeFunction (ref counter);

  ·使用锁

  这是为了锁定代码关键区域以进行同步,锁定代码如下:

  lock (this){ Some statements ;}

  ·使用Monitor

  当有需要进行线程管理的时候可以使用:

  Monitor.Enter(this);

  其他也有一些方法进行管理,这里就不一一提及了。

  线程的缺点

  线程自然也有缺点,以下列出了一些:

  ·如果有大量的线程,会影响性能,因为操作系统需要在他们之间切换;

  ·更多的线程需要更多的内存空间

  ·线程会给程序带来更多的bug,因此要小心使用

  ·线程的中止需要考虑其对程序运行的影响

  ·通常块模型数据是在多个线程间共享的,需要一个合适的锁系统替换掉数据共享