好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

线程池中的工作者线程

线程池中的工作者线程

目录:

一、上节补充

二、CLR线程池基础

三、通过线程池的工作者线程实现异步

四、使用委托实现异步

五、任务

一、上节补充

对于Thread类还有几个常用方法需要说明的。

1.1 Suspend和Resume方法

这两个方法在.net Framework 1.0的时候就支持的方法,他们分别可以挂起线程和恢复挂起的线程。但在.net Framework 2.0以后的版本中这两个方法都过时了,MSDN的解释是这样:

警告:

不要使用 Suspend 和  Resume  方法来同步线程的活动。 您无法知道挂起线程时它正在执行什么代码。 如果您在安全权限评估期间挂起持有锁的线程,则 AppDomain中的其他线程可能被阻止。 如果您在线程正在执行类构造函数时挂起它,则 AppDomain中尝试使用该类的其他线程将被阻止。 这样很容易发生死锁。

对于这个解释可能有点抽象吧,让我们来看看一段代码可能会清晰点:

  class   Program
    {
          static   void  Main( string  [] args)
        {
              //   创建一个线程来测试 
            Thread thread1 =  new   Thread(TestMethod);      
            thread1.Name  =  "  Thread1  "  ;   
            thread1.Start();    
            Thread.Sleep(2  000  );
            Console.WriteLine(  "  Main Thread is running  "  );
              ///  /int b = 0;
              ///  /int a = 3 / b;
              ///  /Console.WriteLine(a); 
             thread1.Resume();     
            Console.Read();
        }

          private   static   void   TestMethod()
        {     
            Console.WriteLine(  "  Thread: {0} has been suspended!  "  , Thread.CurrentThread.Name);
      
              //  将当前线程挂起 
             Thread.CurrentThread.Suspend();          
            Console.WriteLine(  "  Thread: {0} has been resumed!  "  , Thread.CurrentThread.Name);
        }
    } 

在上面这段代码中thread1线程是在主线程中恢复的,但当主线程发生异常时,这时候就thread1一直处于挂起状态,此时thread1所使用的资源就不能释放(除非强制终止进程),当另外线程需要使用这快资源的时候, 这时候就很可能发生死锁现象。

上面一段代码还存在一个隐患,请看下面一小段代码:

  class   Program
    {
          static   void  Main( string  [] args)
        {
              //   创建一个线程来测试 
            Thread thread1 =  new   Thread(TestMethod);      
            thread1.Name  =  "  Thread1  "  ;   
            thread1.Start();
            Console.WriteLine(  "  Main Thread is running  "  );
            thread1.Resume();     
            Console.Read();
        }

          private   static   void   TestMethod()
        {     
            Console.WriteLine(  "  Thread: {0} has been suspended!  "  , Thread.CurrentThread.Name);
            Thread.Sleep(  1000  );

              //  将当前线程挂起 
             Thread.CurrentThread.Suspend();          
            Console.WriteLine(  "  Thread: {0} has been resumed!  "  , Thread.CurrentThread.Name);
        }
    } 

当主线程跑(运行)的太快,做完自己的事情去唤醒thread1时,此时thread1还没有挂起而起唤醒thread1,此时就会出现异常了。并且上面使用的Suspend和Resume方法,编译器已经出现警告了,提示这两个方法已经过时, 所以在我们平时使用中应该尽量避免。

1.2  Abort和 Interrupt方法

Abort方法和Interrupt都是用来终止线程的,但是两者还是有区别的。

1、他们抛出的异常不一样,Abort 方法抛出的异常是ThreadAbortException, Interrupt抛出的异常为ThreadInterruptedException

2、调用interrupt方法的线程之后可以被唤醒,然而调用Abort方法的线程就直接被终止不能被唤醒的。

下面一段代码是掩饰Abort方法的使用

 using   System;
  using   System.Threading;

  namespace   ConsoleApplication1
{
      class   Program
    {
          static   void  Main( string  [] args)
        {
            Thread abortThread  =  new   Thread(AbortMethod);
            abortThread.Name  =  "  Abort Thread  "  ;
            abortThread.Start();
            Thread.Sleep(  1000  );
              try  
            {
                abortThread.Abort();     
            }
              catch   
            {
                Console.WriteLine(  "  {0} Exception happen in Main Thread  "  , Thread.CurrentThread.Name);
                Console.WriteLine(  "  {0} Status is:{1} In Main Thread   "  , Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
            }
              finally  
            {
                Console.WriteLine(  "  {0} Status is:{1} In Main Thread   "  , abortThread.Name, abortThread.ThreadState);
            }

            abortThread.Join();
            Console.WriteLine(  "  {0} Status is:{1}   "  , abortThread.Name, abortThread.ThreadState);
            Console.Read();
           
        }

          private   static   void   AbortMethod()
        {
              try  
            {
                Thread.Sleep(  5000  );
            }
              catch  (Exception e)
            {
                Console.WriteLine(e.GetType().Name);
                Console.WriteLine(  "  {0} Exception happen In Abort Thread  "  , Thread.CurrentThread.Name);
                Console.WriteLine(  "  {0} Status is:{1} In Abort Thread   "  , Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
            }
              finally  
            {
                Console.WriteLine(  "  {0} Status is:{1} In Abort Thread  "  , Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
            }
        }
    } 

运行结果:

从运行结果可以看出,调用Abort方法的线程引发的异常类型为ThreadAbortException, 以及异常只会在 调用Abort方法的线程中发生,而不会在主线程中抛出,并且调用Abort方法后线程的状态不是立即改变为Aborted状态,而是从AbortRequested->Aborted。

Interrupt方法:

 using   System;
  using   System.Threading;

  namespace   ConsoleApplication1
{
      class   Program
    {
          static   void  Main( string  [] args)
        { Thread interruptThread  =  new   Thread(AbortMethod);
            interruptThread.Name  =  "  Interrupt Thread  "  ;
            interruptThread.Start();  
            interruptThread.Interrupt();     
           
            interruptThread.Join();
            Console.WriteLine(  "  {0} Status is:{1}   "  , interruptThread.Name, interruptThread.ThreadState);
            Console.Read();     
        }

          private   static   void   AbortMethod()
        {
              try  
            {
                Thread.Sleep(  5000  );
            }
              catch  (Exception e)
            {
                Console.WriteLine(e.GetType().Name);
                Console.WriteLine(  "  {0} Exception happen In Interrupt Thread  "  , Thread.CurrentThread.Name);
                Console.WriteLine(  "  {0} Status is:{1} In Interrupt Thread   "  , Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
            }
              finally  
            {
                Console.WriteLine(  "  {0} Status is:{1} In Interrupt Thread  "  , Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
            }

        }
    }
} 

运行结果:

从结果中可以得到,调用Interrupt方法抛出的异常为:ThreadInterruptException, 以及当调用Interrupt方法后线程的状态应该是中断的, 但是从运行结果看此时的线程因为了Join,Sleep方法而唤醒了线程,为了进一步解释调用Interrupt方法的线程可以被唤醒, 我们可以在线程执行的方法中运用循环,如果线程可以唤醒,则输出结果中就一定会有循环的部分,然而调用Abort方法线程就直接终止,就不会有循环的部分,下面代码相信大家看后肯定会更加理解两个方法的区别的:

 using   System;
  using   System.Threading;

  namespace   ConsoleApplication2
{
      class   Program
    {
          static   void  Main( string  [] args)
        {
            Thread thread1  =  new   Thread(TestMethod);
            thread1.Start();
            Thread.Sleep(  100  );

            thread1.Interrupt();
            Thread.Sleep(  3000  );
            Console.WriteLine(  "  after finnally block, the Thread1 status is:{0}  "  , thread1.ThreadState);
            Console.Read();
        }
          private   static   void   TestMethod()
        {
            
              for  ( int  i =  0 ; i <  4 ; i++ )
            {
                  try  
                {
                    Thread.Sleep(  2000  );
                    Console.WriteLine(  "  Thread is Running  "  );
                }
                  catch   (Exception e)
                {
                      if  (e !=  null  )
                    {
                        Console.WriteLine(  "  Exception {0} throw   "  , e.GetType().Name);
                    }
                }
                  finally  
                {
                    Console.WriteLine(  "  Current Thread status is:{0}   "  , Thread.CurrentThread.ThreadState);
                }
            }
        }
    }
} 

运行结果为:

如果把上面的 thread1.Interrupt();改为 thread1.Abort(); 运行结果为:

二、线程池基础

首先,创建和销毁线程是一个要耗费大量时间的过程,另外,太多的线程也会浪费内存资源,所以通过Thread类来创建过多的线程反而有损于性能,为了改善这样的问题 ,.net中就引入了线程池。

线程池形象的表示就是存放应用程序中使用的线程的一个集合(就是放线程的地方,这样线程都放在一个地方就好管理了)。CLR初始化时,线程池中是没有线程的,在内部, 线程池维护了一个操作请求队列,当应用程序想执行一个异步操作时,就调用一个方法,就将一个任务放到线程池的队列中,线程池中代码从队列中提取任务,将这个任务委派给一个线程池线程去执行,当线程池线程完成任务时,线程不会被销毁,而是返回到线程池中,等待响应另一个请求。由于线程不被销毁, 这样就可以避免因为创建线程所产生的性能损失。

注意:通过线程池创建的线程默认为后台线程,优先级默认为Normal.

三、通过线程池的工作者线程实现异步

3.1 创建工作者线程的方法

public static bool QueueUserWorkItem (WaitCallback callBack);

public static bool QueueUserWorkItem(WaitCallback callback, Object state);

这两个方法向线程池的队列添加一个工作项(work item)以及一个可选的状态数据。然后,这两个方法就会立即返回。

工作项其实就是由callback参数标识的一个方法,该方法将由线程池线程执行。同时写的回调方法必须匹配System.Threading.WaitCallback委托类型,定义为:

public delegate void WaitCallback(Object state);

下面演示如何通过线程池线程来实现异步调用:

 using   System;
  using   System.Threading;

  namespace   ThreadPoolUse
{
      class   Program
    {
          static   void  Main( string  [] args)
        {
              //   设置线程池中处于活动的线程的最大数目
              //   设置线程池中工作者线程数量为1000,I/O线程数量为1000 
            ThreadPool.SetMaxThreads( 1000 ,  1000  );
            Console.WriteLine(  "  Main Thread: queue an asynchronous method  "  );
            PrintMessage(  "  Main Thread Start  "  );

              //   把工作项添加到队列中,此时线程池会用工作者线程去执行回调方法 
             ThreadPool.QueueUserWorkItem(asyncMethod);
            Console.Read();
        }

          //   方法必须匹配WaitCallback委托 
         private   static   void  asyncMethod( object   state)
        {
            Thread.Sleep(  1000  );
            PrintMessage(  "  Asynchoronous Method  "  );
            Console.WriteLine(  "  Asynchoronous thread has worked   "  );
        }

          //   打印线程池信息 
         private   static   void   PrintMessage(String data)
        {
              int   workthreadnumber;
              int   iothreadnumber;

              //   获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
              //   获得的可用I/O线程数量给iothreadnumber变量 
            ThreadPool.GetAvailableThreads( out  workthreadnumber,  out   iothreadnumber);

            Console.WriteLine(  "  {0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n  "  ,
                data,
                Thread.CurrentThread.ManagedThreadId, 
                Thread.CurrentThread.IsBackground.ToString(),
                workthreadnumber.ToString(),
                iothreadnumber.ToString());
        }
    }
} 

运行结果:

从结果中可以看出,线程池中的可用的工作者线程少了一个,用去执行回调方法了。

ThreadPool.QueueUserWorkItem(WaitCallback callback,Object state) 方法可以把object对象作为参数传送到回调函数中,使用和 ThreadPool.QueueUserWorkItem(WaitCallback callback)的使用和类似,这里就不列出了。

3.2   协作式取消

.net Framework提供了 取消操作 的模式, 这个模式是协作式的。为了取消一个操作,首先必须创建一个 System.Threading.CancellationTokenSource 对象。

下面代码演示了协作式取消的使用,主要实现当用户在控制台敲下回车键后就停止数数方法。

 using   System;
  using   System.Collections.Generic;
  using   System.Linq;
  using   System.Text;
  using   System.Threading;

  namespace   ConsoleApplication3
{
      class   Program
    {
          static   void  Main( string  [] args)
        {
            ThreadPool.SetMaxThreads(  1000 ,  1000  );
            Console.WriteLine(  "  Main thread run  "  );    
            PrintMessage(  "  Start  "  );
            Run();
            Console.ReadKey();
        }

          private   static   void   Run()
        {
            CancellationTokenSource cts  =  new   CancellationTokenSource();

              //   这里用Lambda表达式的方式和使用委托的效果一样的,只是用了Lambda后可以少定义一个方法。
              //   这在这里就是让大家明白怎么lambda表达式如何由委托转变的 
             ///  /ThreadPool.QueueUserWorkItem(o => Count(cts.Token, 1000)); 
 
            ThreadPool.QueueUserWorkItem(callback, cts.Token);

            Console.WriteLine(  "  Press Enter key to cancel the operation\n  "  );
            Console.ReadLine();

              //   传达取消请求 
             cts.Cancel();
        }
        
          private   static   void  callback( object   state)
        {
            Thread.Sleep(  1000  );
            PrintMessage(  "  Asynchoronous Method Start  "  );
            CancellationToken token  = (CancellationToken)state;    
            Count(token,   1000  );
        }

          //   执行的操作,当受到取消请求时停止数数 
         private   static   void  Count(CancellationToken token, int   countto)
        {
              for  ( int  i =  0 ; i < countto; i++ )
            {
                  if   (token.IsCancellationRequested)
                {
                    Console.WriteLine(  "  Count is canceled  "  );
                      break  ;
                }

                Console.WriteLine(i);
                Thread.Sleep(  300  );
            }
            
            Console.WriteLine(  "  Cout has done  "  );       
        }

          //   打印线程池信息 
         private   static   void   PrintMessage(String data)
        {
              int   workthreadnumber;
              int   iothreadnumber;

              //   获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
              //   获得的可用I/O线程数量给iothreadnumber变量 
            ThreadPool.GetAvailableThreads( out  workthreadnumber,  out   iothreadnumber);

            Console.WriteLine(  "  {0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n  "  ,
                data,
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsBackground.ToString(),
                workthreadnumber.ToString(),
                iothreadnumber.ToString());
        }
    }
} 

运行结果:

四、使用委托实现异步

通过调用ThreadPool的QueueUserWorkItem方法来来启动工作者线程非常方便,但委托WaitCallback指向的是带有一个参数的无返回值的方法,如果我们实际操作中需要有返回值,或者需要带有多个参数, 这时通过这样的方式就难以实现, 为了解决这样的问题,我们可以通过委托来建立工作这线程,

下面代码演示了使用委托如何实现异步:

 using   System;
  using   System.Threading;

  namespace   Delegate
{
      class   Program
    {
          //   使用委托的实现的方式是使用了异步变成模型APM(Asynchronous Programming Model)
          //   自定义委托 
         private   delegate   string   MyTestdelegate();

          static   void  Main( string  [] args)
        {
            ThreadPool.SetMaxThreads(  1000 ,  1000  );
            PrintMessage(  "  Main Thread Start  "  );

              //  实例化委托 
            MyTestdelegate testdelegate =  new   MyTestdelegate(asyncMethod);

              //   异步调用委托 
            IAsyncResult result = testdelegate.BeginInvoke( null ,  null  );

              //   获取结果并打印出来 
             string  returndata =  testdelegate.EndInvoke(result);
            Console.WriteLine(returndata);

            Console.ReadLine();
        }
          private   static   string   asyncMethod()
        {
            Thread.Sleep(  1000  );
            PrintMessage(  "  Asynchoronous Method  "  );
              return   "  Method has completed  "  ;
        }

          //   打印线程池信息 
         private   static   void   PrintMessage(String data)
        {
              int   workthreadnumber;
              int   iothreadnumber;

              //   获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
              //   获得的可用I/O线程数量给iothreadnumber变量 
            ThreadPool.GetAvailableThreads( out  workthreadnumber,  out   iothreadnumber);

            Console.WriteLine(  "  {0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n  "  ,
                data,
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsBackground.ToString(),
                workthreadnumber.ToString(),
                iothreadnumber.ToString());
        }
    }
} 

运行结果:

五、任务

同样 任务的引入也是为了解决通过ThreadPool.QueueUserWorkItem中限制的问题,

下面代码演示通过任务来实现异步:

5.1 使用任务来实现异步

 using   System;
  using   System.Threading;
  using   System.Threading.Tasks;

  namespace   TaskUse
{
      class   Program
    {
          static   void  Main( string  [] args)
        {
            ThreadPool.SetMaxThreads(  1000 ,  1000  );
            PrintMessage(  "  Main Thread Start  "  );
              //   调用构造函数创建Task对象, 
            Task< int > task =  new  Task< int >(n => asyncMethod(( int )n),  10  );

              //   启动任务  
             task.Start();
              //   等待任务完成 
             task.Wait();
            Console.WriteLine(  "  The Method result is:   " + task.Result);

            Console.ReadLine();
        }

          private   static   int  asyncMethod( int   n)
        {
            Thread.Sleep(  1000  );
            PrintMessage(  "  Asynchoronous Method  "  );

              int  sum =  0  ;
              for  ( int  i =  1 ; i < n; i++ )
            {
                  //   如果n太大,使用checked使下面代码抛出异常 
                 checked  
                {
                    sum  +=  i;
                }
            }

              return   sum;
        }

          //   打印线程池信息 
         private   static   void   PrintMessage(String data)
        {
              int   workthreadnumber;
              int   iothreadnumber;

              //   获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
              //   获得的可用I/O线程数量给iothreadnumber变量 
            ThreadPool.GetAvailableThreads( out  workthreadnumber,  out   iothreadnumber);

            Console.WriteLine(  "  {0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n  "  ,
                data,
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsBackground.ToString(),
                workthreadnumber.ToString(),
                iothreadnumber.ToString());
        }
    }
} 


运行结果:

5.2 取消任务

如果要取消任务, 同样可以使用一个CancellationTokenSource对象来取消一个Task.

下面代码演示了如何来取消一个任务:

 using   System;
  using   System.Threading;
  using   System.Threading.Tasks;

  namespace   TaskUse
{
      class   Program
    {
          static   void  Main( string  [] args)
        {
            ThreadPool.SetMaxThreads(  1000 ,  1000  );
            PrintMessage(  "  Main Thread Start  "  );
            CancellationTokenSource cts  =  new   CancellationTokenSource();

              //   调用构造函数创建Task对象,将一个CancellationToken传给Task构造器从而使Task和CancellationToken关联起来 
            Task< int > task =  new  Task< int >(n => asyncMethod(cts.Token, ( int )n),  10  );

              //   启动任务  
             task.Start();

              //   延迟取消任务 
            Thread.Sleep( 3000  );

              //   取消任务 
             cts.Cancel();
            Console.WriteLine(  "  The Method result is:   "  +  task.Result);
            Console.ReadLine();
        }

          private   static   int  asyncMethod(CancellationToken ct,  int   n)
        {
            Thread.Sleep(  1000  );
            PrintMessage(  "  Asynchoronous Method  "  );

              int  sum =  0  ;
              try  
            {
                  for  ( int  i =  1 ; i < n; i++ )
                {
                      //   当CancellationTokenSource对象调用Cancel方法时,
                      //   就会引起OperationCanceledException异常
                      //   通过调用CancellationToken的ThrowIfCancellationRequested方法来定时检查操作是否已经取消,
                      //   这个方法和CancellationToken的IsCancellationRequested属性类似 
                     ct.ThrowIfCancellationRequested();
                    Thread.Sleep(  500  );
                      //   如果n太大,使用checked使下面代码抛出异常 
                     checked  
                    {
                        sum  +=  i;
                    }
                }
            }
              catch   (Exception e)
            {
                Console.WriteLine(  "  Exception is:  "  +  e.GetType().Name);
                Console.WriteLine(  "  Operation is Canceled  "  );
            }

              return   sum;
        }

          //   打印线程池信息 
         private   static   void   PrintMessage(String data)
        {
              int   workthreadnumber;
              int   iothreadnumber;

              //   获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
              //   获得的可用I/O线程数量给iothreadnumber变量 
            ThreadPool.GetAvailableThreads( out  workthreadnumber,  out   iothreadnumber);

            Console.WriteLine(  "  {0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n  "  ,
                data,
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsBackground.ToString(),
                workthreadnumber.ToString(),
                iothreadnumber.ToString());
        }
    }
} 

运行结果:

5.3 任务工厂

同样可以通过任务工厂TaskFactory类型来实现异步操作。

 using   System;
  using   System.Threading;
  using   System.Threading.Tasks;

  namespace   TaskFactory
{
      class   Program
    {
          static   void  Main( string  [] args)
        {
            ThreadPool.SetMaxThreads(  1000 ,  1000  );
            Task.Factory.StartNew(()  => PrintMessage( "  Main Thread  "  )); 
            Console.Read();
        }
          //   打印线程池信息 
         private   static   void   PrintMessage(String data)
        {
              int   workthreadnumber;
              int   iothreadnumber;

              //   获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量
              //   获得的可用I/O线程数量给iothreadnumber变量 
            ThreadPool.GetAvailableThreads( out  workthreadnumber,  out   iothreadnumber);

            Console.WriteLine(  "  {0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n  "  ,
                data,
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsBackground.ToString(),
                workthreadnumber.ToString(),
                iothreadnumber.ToString());
        }
    }
} 

运行结果:

讲到这里CLR的工作者线程大致讲完了,希望也篇文章可以让大家对线程又有进一步的理解。在后面的一篇线程系列将谈谈CLR线程池的I/O线程。

作者:Learnig hard 出处:http://HdhCmsTestcnblogs测试数据/zhili/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

分类:  C# .Net ,  CLR

标签:  ThreadPool 工作者线程 多线程

作者: Leo_wl

    

出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于线程池中的工作者线程的详细内容...

  阅读:40次

上一篇: .net生成Html分页的代码

下一篇:vim test