[C# 线程处理系列]专题三:线程池中的工作者线程

http://blog.csdn.net/learning_hard/article/details/8991314


目录:

一、CLR线程池基础

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

三、使用委托实现异步

四、任务


一、线程池基础

首先,创建和销毁线程是一个要耗费大量时间的过程,另外,太多的线程也会浪费内存资源,所以通过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);

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

[csharp]  view plain copy print ?
  1. using System;  
  2. using System.Threading;  
  3.   
  4. namespace ThreadPoolUse  
  5. {  
  6.     class Program  
  7.     {  
  8.         static void Main(string[] args)  
  9.         {  
  10.             // 设置线程池中处于活动的线程的最大数目  
  11.             // 设置线程池中工作者线程数量为1000,I/O线程数量为1000  
  12.             ThreadPool.SetMaxThreads(1000, 1000);  
  13.             Console.WriteLine("Main Thread: queue an asynchronous method");  
  14.             PrintMessage("Main Thread Start");  
  15.   
  16.             // 把工作项添加到队列中,此时线程池会用工作者线程去执行回调方法  
  17.             ThreadPool.QueueUserWorkItem(asyncMethod);  
  18.             Console.Read();  
  19.         }  
  20.   
  21.         // 方法必须匹配WaitCallback委托  
  22.         private static void asyncMethod(object state)  
  23.         {  
  24.             Thread.Sleep(1000);  
  25.             PrintMessage("Asynchoronous Method");  
  26.             Console.WriteLine("Asynchoronous thread has worked ");  
  27.         }  
  28.   
  29.         // 打印线程池信息  
  30.         private static void PrintMessage(String data)  
  31.         {  
  32.             int workthreadnumber;  
  33.             int iothreadnumber;  
  34.   
  35.             // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量  
  36.             // 获得的可用I/O线程数量给iothreadnumber变量  
  37.             ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);  
  38.   
  39.             Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n",  
  40.                 data,  
  41.                 Thread.CurrentThread.ManagedThreadId,   
  42.                 Thread.CurrentThread.IsBackground.ToString(),  
  43.                 workthreadnumber.ToString(),  
  44.                 iothreadnumber.ToString());  
  45.         }  
  46.     }  
  47. }  

运行结果:

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

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

3.2 协作式取消

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

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

[csharp]  view plain copy print ?
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Threading;  
  6.   
  7. namespace ConsoleApplication3  
  8. {  
  9.     class Program  
  10.     {  
  11.         static void Main(string[] args)  
  12.         {  
  13.             ThreadPool.SetMaxThreads(1000, 1000);  
  14.             Console.WriteLine("Main thread run");      
  15.             PrintMessage("Start");  
  16.             Run();  
  17.             Console.ReadKey();  
  18.         }  
  19.   
  20.         private static void Run()  
  21.         {  
  22.             CancellationTokenSource cts = new CancellationTokenSource();  
  23.   
  24.             // 这里用Lambda表达式的方式和使用委托的效果一样的,只是用了Lambda后可以少定义一个方法。  
  25.             // 这在这里就是让大家明白怎么lambda表达式如何由委托转变的  
  26.             ThreadPool.QueueUserWorkItem(o => Count(cts.Token, 1000));  
  27.   
  28.             ThreadPool.QueueUserWorkItem(callback, cts.Token);  
  29.   
  30.             Console.WriteLine("Press Enter key to cancel the operation\n");  
  31.             Console.ReadLine();  
  32.   
  33.             // 传达取消请求  
  34.             cts.Cancel();  
  35.         }  
  36.           
  37.         private static void callback(object state)  
  38.         {  
  39.             Thread.Sleep(1000);  
  40.             PrintMessage("Asynchoronous Method Start");  
  41.             CancellationToken token =(CancellationToken)state;      
  42.             Count(token, 1000);  
  43.         }  
  44.   
  45.         // 执行的操作,当受到取消请求时停止数数  
  46.         private static void Count(CancellationToken token,int countto)  
  47.         {  
  48.             for (int i = 0; i < countto; i++)  
  49.             {  
  50.                 if (token.IsCancellationRequested)  
  51.                 {  
  52.                     Console.WriteLine("Count is canceled");  
  53.                     break;  
  54.                 }  
  55.   
  56.                 Console.WriteLine(i);  
  57.                 Thread.Sleep(300);  
  58.             }  
  59.               
  60.             Console.WriteLine("Cout has done");         
  61.         }  
  62.   
  63.         // 打印线程池信息  
  64.         private static void PrintMessage(String data)  
  65.         {  
  66.             int workthreadnumber;  
  67.             int iothreadnumber;  
  68.   
  69.             // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量  
  70.             // 获得的可用I/O线程数量给iothreadnumber变量  
  71.             ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);  
  72.   
  73.             Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n",  
  74.                 data,  
  75.                 Thread.CurrentThread.ManagedThreadId,  
  76.                 Thread.CurrentThread.IsBackground.ToString(),  
  77.                 workthreadnumber.ToString(),  
  78.                 iothreadnumber.ToString());  
  79.         }  
  80.     }  
  81. }  

运行结果:

 

四、使用委托实现异步

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

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

[csharp]  view plain copy print ?
  1. using System;  
  2. using System.Threading;  
  3.   
  4. namespace Delegate  
  5. {  
  6.     class Program  
  7.     {  
  8.         // 使用委托的实现的方式是使用了异步变成模型APM(Asynchronous Programming Model)  
  9.         // 自定义委托  
  10.         private delegate string MyTestdelegate();  
  11.   
  12.         static void Main(string[] args)  
  13.         {  
  14.             ThreadPool.SetMaxThreads(1000, 1000);  
  15.             PrintMessage("Main Thread Start");  
  16.   
  17.             //实例化委托  
  18.             MyTestdelegate testdelegate = new MyTestdelegate(asyncMethod);  
  19.   
  20.             // 异步调用委托  
  21.             IAsyncResult result = testdelegate.BeginInvoke(nullnull);  
  22.   
  23.             // 获取结果并打印出来  
  24.             string returndata = testdelegate.EndInvoke(result);  
  25.             Console.WriteLine(returndata);  
  26.   
  27.             Console.ReadLine();  
  28.         }  
  29.         private static string asyncMethod()  
  30.         {  
  31.             Thread.Sleep(1000);  
  32.             PrintMessage("Asynchoronous Method");  
  33.             return "Method has completed";  
  34.         }  
  35.   
  36.         // 打印线程池信息  
  37.         private static void PrintMessage(String data)  
  38.         {  
  39.             int workthreadnumber;  
  40.             int iothreadnumber;  
  41.   
  42.             // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量  
  43.             // 获得的可用I/O线程数量给iothreadnumber变量  
  44.             ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);  
  45.   
  46.             Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n",  
  47.                 data,  
  48.                 Thread.CurrentThread.ManagedThreadId,  
  49.                 Thread.CurrentThread.IsBackground.ToString(),  
  50.                 workthreadnumber.ToString(),  
  51.                 iothreadnumber.ToString());  
  52.         }  
  53.     }  
  54. }  

运行结果:

 

五、任务

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

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

5.1 使用任务来实现异步

[csharp]  view plain copy print ?
  1. using System;  
  2. using System.Threading;  
  3. using System.Threading.Tasks;  
  4.   
  5. namespace TaskUse  
  6. {  
  7.     class Program  
  8.     {  
  9.         static void Main(string[] args)  
  10.         {  
  11.             ThreadPool.SetMaxThreads(1000, 1000);  
  12.             PrintMessage("Main Thread Start");  
  13.             // 调用构造函数创建Task对象,  
  14.             Task<int> task = new Task<int>(n => asyncMethod((int)n), 10);  
  15.   
  16.             // 启动任务   
  17.             task.Start();  
  18.             // 等待任务完成  
  19.             task.Wait();  
  20.             Console.WriteLine("The Method result is: "+task.Result);  
  21.   
  22.             Console.ReadLine();  
  23.         }  
  24.   
  25.         private static int asyncMethod(int n)  
  26.         {  
  27.             Thread.Sleep(1000);  
  28.             PrintMessage("Asynchoronous Method");  
  29.   
  30.             int sum = 0;  
  31.             for (int i = 1; i < n; i++)  
  32.             {  
  33.                 // 如果n太大,使用checked使下面代码抛出异常  
  34.                 checked  
  35.                 {  
  36.                     sum += i;  
  37.                 }  
  38.             }  
  39.   
  40.             return sum;  
  41.         }  
  42.   
  43.         // 打印线程池信息  
  44.         private static void PrintMessage(String data)  
  45.         {  
  46.             int workthreadnumber;  
  47.             int iothreadnumber;  
  48.   
  49.             // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量  
  50.             // 获得的可用I/O线程数量给iothreadnumber变量  
  51.             ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);  
  52.   
  53.             Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n",  
  54.                 data,  
  55.                 Thread.CurrentThread.ManagedThreadId,  
  56.                 Thread.CurrentThread.IsBackground.ToString(),  
  57.                 workthreadnumber.ToString(),  
  58.                 iothreadnumber.ToString());  
  59.         }  
  60.     }  
  61. }  

运行结果:

5.2 取消任务

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

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

[csharp]  view plain copy print ?
  1. using System;  
  2. using System.Threading;  
  3. using System.Threading.Tasks;  
  4.   
  5. namespace TaskUse  
  6. {  
  7.     class Program  
  8.     {  
  9.         static void Main(string[] args)  
  10.         {  
  11.             ThreadPool.SetMaxThreads(1000, 1000);  
  12.             PrintMessage("Main Thread Start");  
  13.             CancellationTokenSource cts = new CancellationTokenSource();  
  14.   
  15.             // 调用构造函数创建Task对象,将一个CancellationToken传给Task构造器从而使Task和CancellationToken关联起来  
  16.             Task<int> task = new Task<int>(n => asyncMethod(cts.Token, (int)n), 10);  
  17.   
  18.             // 启动任务   
  19.             task.Start();  
  20.   
  21.             // 延迟取消任务  
  22.             Thread.Sleep(3000);  
  23.   
  24.             // 取消任务  
  25.             cts.Cancel();  
  26.             Console.WriteLine("The Method result is: " + task.Result);  
  27.             Console.ReadLine();  
  28.         }  
  29.   
  30.         private static int asyncMethod(CancellationToken ct, int n)  
  31.         {  
  32.             Thread.Sleep(1000);  
  33.             PrintMessage("Asynchoronous Method");  
  34.   
  35.             int sum = 0;  
  36.             try  
  37.             {  
  38.                 for (int i = 1; i < n; i++)  
  39.                 {  
  40.                     // 当CancellationTokenSource对象调用Cancel方法时,  
  41.                     // 就会引起OperationCanceledException异常  
  42.                     // 通过调用CancellationToken的ThrowIfCancellationRequested方法来定时检查操作是否已经取消,  
  43.                     // 这个方法和CancellationToken的IsCancellationRequested属性类似  
  44.                     ct.ThrowIfCancellationRequested();  
  45.                     Thread.Sleep(500);  
  46.                     // 如果n太大,使用checked使下面代码抛出异常  
  47.                     checked  
  48.                     {  
  49.                         sum += i;  
  50.                     }  
  51.                 }  
  52.             }  
  53.             catch (Exception e)  
  54.             {  
  55.                 Console.WriteLine("Exception is:" + e.GetType().Name);  
  56.                 Console.WriteLine("Operation is Canceled");  
  57.             }  
  58.   
  59.             return sum;  
  60.         }  
  61.   
  62.         // 打印线程池信息  
  63.         private static void PrintMessage(String data)  
  64.         {  
  65.             int workthreadnumber;  
  66.             int iothreadnumber;  
  67.   
  68.             // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量  
  69.             // 获得的可用I/O线程数量给iothreadnumber变量  
  70.             ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);  
  71.   
  72.             Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n",  
  73.                 data,  
  74.                 Thread.CurrentThread.ManagedThreadId,  
  75.                 Thread.CurrentThread.IsBackground.ToString(),  
  76.                 workthreadnumber.ToString(),  
  77.                 iothreadnumber.ToString());  
  78.         }  
  79.     }  
  80. }  

运行结果:

 

5.3 任务工厂

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

[csharp]  view plain copy print ?
  1. using System;  
  2. using System.Threading;  
  3. using System.Threading.Tasks;  
  4.   
  5. namespace TaskFactory  
  6. {  
  7.     class Program  
  8.     {  
  9.         static void Main(string[] args)  
  10.         {  
  11.             ThreadPool.SetMaxThreads(1000, 1000);  
  12.             Task.Factory.StartNew(() => PrintMessage("Main Thread"));   
  13.             Console.Read();  
  14.         }  
  15.         // 打印线程池信息  
  16.         private static void PrintMessage(String data)  
  17.         {  
  18.             int workthreadnumber;  
  19.             int iothreadnumber;  
  20.   
  21.             // 获得线程池中可用的线程,把获得的可用工作者线程数量赋给workthreadnumber变量  
  22.             // 获得的可用I/O线程数量给iothreadnumber变量  
  23.             ThreadPool.GetAvailableThreads(out workthreadnumber, out iothreadnumber);  
  24.   
  25.             Console.WriteLine("{0}\n CurrentThreadId is {1}\n CurrentThread is background :{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is: {4}\n",  
  26.                 data,  
  27.                 Thread.CurrentThread.ManagedThreadId,  
  28.                 Thread.CurrentThread.IsBackground.ToString(),  
  29.                 workthreadnumber.ToString(),  
  30.                 iothreadnumber.ToString());  
  31.         }  
  32.     }  
  33. }  

运行结果:

 

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

版权声明:本文为博主原创文章,未经博主允许不得转载。

0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值