线程池是你的应用程序能使用的线程集合。每CLR —个线程池;这个线程池由CLR控制 的所有AppDomain共享。如果一个进程中加载了多个CLR,那么每个CLR都有它自己的线程池。
CLR初始化时,线程池中是没有线程的。在内部,线程池维护了一个操作请求队列。应用 程序执行一个异步操作时,就调用某个方法,将一个记录项(entry)追加到线程池的队列中。 线程池的代码从这个队列中提取记录项,将这个记录项派发(dispatch)给一个线程池线程。 如果线程池中没有线程,就创建一个新线程。创建线程会造成一定的性能损失。然而,当线程池线程完成任务后,线程不会被销毁。相反,线程会返回线程池,在那里进入空闲状态,等待响应另一个请求。由于线程不销毁自身,所以不再产生额外的性能损失。
如果你的应用程序向线程池发出许多请求,线程池会尝试只用这一个线程来服务所有请求。
如果你的应用程序发出请求的速度超过了线程池线程处理它们的速度,就会创建额 外的线程。
线程池是启 发式的。如果应用程序需要执行许多任务,同时有可用的CPU,那么线程池会创建更多的 线程。应用程序负载减轻,线程池线程就终止它们自己。
要将一个异步的计算限制操作放到线程池的队列中,通常吋以调用ThreadPool类定义的以
下方法之一:
static Boolean QueueUserWorkltem(WaitCallback callBack);
static Boolean QueueUserWork工tem《WaitCallback callBack, Object state);
这些方法向线程池的队列添加一个“工作项” (work item)以及可选的状态数据。然后,所有方法会立即返回。工作项其实就是由callBack参数标识的一个方法,该方法将由线程池 线程调用。可向方法传递一个state实参(状态数据)。无state参数的那个版本的QueueUserWorkltem则向回调方法传递null。最终,池中的某个线程会处理工作项,造成 你指定的方法被调用。你写的回调方法必须匹配System.Threading.WaitCalIback委托类型,后者的定义如下:
delegate void WaitCallback(Object state);
using System;
using System.Threading;
public static class Program {
public static void Main() {
Console.WriteLine ("Main thread: queuing an asynchronous operation");
ThreadPool.QueueUserWorkltem(ComputeBoundOp, 5);
Console.WriteLine ("Main thread: Doing other work here.");
Thread.Sleep(10000); // 模拟其他工作(10 秒〉
Console.WriteLine ("Hit〈Enter> to end this program. ..");
Console.ReadLine();
//这个方法的签名必须匹配WaitCallback委托
private static void ComputeBoundOp(Object state) {
//这个方法山一个线程池线秤执行
Console.WriteLine("In ComputeBoundOp: state:{0}", state);
Thread.Sleep (1000) ; // 模&其他工作(1 秒)
//这个方法返回后.线程回到池中.等待另一个任务
}
}
编译并运行上述代码得到以下输出:
Main thread: queuing an asynchronous operation
Main thread: Doing other work here...
In ComputeBoundOp: state=5
有时也得到以下输出:
Main thread: queuing an asynchronous operation
In ComputeBoundOp: state=5
Main thread: Doing other work here...
之所以输出行的顺序会发生变化,是因为两个方法相互之间是异步运行的。Windows调度
器决定先调度哪一个线程。如果应用程序在多核机器上运行,可能同时调度它们。
System.Threading命名空间有一个ExecutionContext类,它允许你控制线程的执行上下文
如何从一个线程“流”向另一个。下面展示了这个类的样子:
public sealed class Executioncontext:IDisposable,ISerializable (
[SecurityCritical]
public static AsyncFlowControl SuppressFlow();
public static void RestoreFlow();
public static Boolean IsFlowSuppressed();
//未列出不常用的方法
}
下例展示了向CLR的线程池队列添加一个工作项的时候,如何通过阻止执行上下文的流动
来影响线程逻辑调用上下文中的数据%
public static void Main() {
//将一些数据放到Main线程的逻辑调用上下文中
CallContext.LogicalSetData("Name={0}","Jeffrey");
//初始化要Eh—个线程池线程做的一些工作,
//线程池线程能访问逻辑调用上F文数据
ThreadPool.QueueUserWorkltem(
state=>Console.WriteLine("Name={0}",CallContext.LogicalGetData("Name")));
//现在,阻lk Main线程的执行上下文的流动
Executioncontext.SuppressFlow();
//初始化要山线程池线程做的工作.
//线程池线程不能汸问逻辑调用上F文数据
ThreadPool.QueueUserWorkltem(
state=>Console.WriteLine("Name={0}",CallContext.LogicalGetData("Name")));
//恢复Main线程的执行上下文的流动,
//以免将來使用史多的线程池线秤
Executioncontext.RestoreFlow();
Console.ReadLine();
}
编译并运行上述代码得到以下输出:
Name=Jeffrey
Name=
取消操作首先要创建一个System.Threading.CancellationTokenSource对象。这个类看起来
public sealed class CancellationTokenSource:IDisposable
{
// 一个引用类型
public CancellationTokenSource();
public void Dispose(); // 释放资源
public Boolean IsCancellationRequested{ get;}
public CancellationToken Token {get;}
public void Cancel(); // 内部调用 Cancel 并传递 false
public void Cancel(Boolean throwOnFirstException);
}
这个对象包含了和管理取消有关的所有状态。构造好一个CanceUationTokenSource(一个引用类型)之后,可从它的Token属性获得一个或多个CancellationToken(—个值类型)实例,并传给你的操作,使操作可以取消。以下是CancellationToken值类型最有用的成员:
public struct CancellationToken ( // 一个值类型
public static CancellationToken None {get;} // 很好用
public Boolean IsCancellationRequested {get;} // 由非通过 Task 调用的操作调用
public void ThrowlfCancellationRequested () ; // 由通过 Task 调用的操作调用
// CancellationTokenSource 取消时.WaitHandle 会收到信号
public WaitHandle WaitHandle {get;}
// GetHashCode, Equals, operator==和 operator!=成员未列出
public Boolean CanBeCanceled {get;} // 很少使用
public CancellationTokenRegistration Register(Action<Object> callback, Object state,
Boolean useSynchronizationContext) ; // 未列出史简单的重载版本
}
CancellationToken实例是轻量级值类型,包含单个私有字段,即对其
CancellationTokenSource对象的引用。在计算限制操作的循环中,可定时调用
CancellationToken的IsCancellationRequested属性,了解循环是否应该提前终止,从而终
止计算限制的操作。提前终止的好处在于,CPU不需要再把时间浪费在你对结果不感兴趣
的操作上。以下代码将这些概念全部梳理了一遍:
internal static class CancellationDemo {
public static void Go() {
CancellationTokenSource cts = new CancellationTokenSource();
// 将CancellationToken 和“要数到的数” (number-to-count-to)传入操作
ThreadPool.QueueUserWorkltem(o=>Count(cts.Token, 1000));
Console.WriteLine("Press to cancel the operation." );
Console.ReadLine();
cts.CancelO ; //如果Count方法己返回,Cancel没有任何效果
// Cance1立即返回,方法从这里继续运行...
Console.ReadLine();
}
private static void Count(CancellationToken token,Int32 countTo) {
for (Int32 count=0; count<countTo; count++) {
if (token.IsCancellationRequested) {
Console.WriteLine(MCount is cancelled");
break; //退出循环以停止操作
}
Console.WriteLine(count);
}
Thread.Sleep (200); //出于演示目的而浪费一些时间
Console.WriteLine("Count is done");
}
}