线程池基础
1,线程的创建和销毁是一个昂贵的操作,线程调度以及上下文切换耗费时间和内存资源。
2,线程池是一个线程集合,供应你的用程序使用。
3,每个CLR有一个自己的线程池,线程池由CLR控制的所有的AppDomain共享。
4,CLR初始化的时候,线程池没有线程的。
5,线程池维护一个操作请求队列。当应用程序想要执行一个一步操作的时候,就调用某个方法。将记录项(empty)追加到线程池队列中,然后线程池代码从队列中提取这个记录项,然后将记录项派遣(dispatch)给一个线程池的线程。当线程池线程完成任务后,不会被销毁。该线程会继续返回到线程池中,进入空闲状态,等待另一个请求。
6,多个应用程序向线程池发出多个请求的时候,只会尝试用一个线程来服务,当超过了线程池线程处理速度的时候,会创建额外的线程。
7,当线程池线程空闲一段时间之后,会自己醒来终止自己释放资源
8,线程池分为工作线程(worker)或者I/O线程,工作线程用于操作异步计算限制操作,I/O线程用于通知代码一个异步I/O限制操作已完成。异步编程模型:(Asychronous Programming Model APM)
执行简单的计算限制操作
计算限制操作:指需要长时间的计算的线程
1,要将一个异步,计算限制的操作放到线程池的队列中(向线程池添加一个“工作项”),调用ThreadPool类的几个方法:
static Boolean QueueUserWorkItem(WaitCallback callBack)
static Boolean QueueUserWorkItem(WaitCallback callBack,Object state)
2,线程池以异步调用一个方法
using System;
using System.Threading;
public static class Program{
public static void Main(){
Console.WriteLine("Main thread:queing an asynchronous operation");
TreadPool.QueueUserWorkItem(ComputeBoundOp,5);
Console.WriteLine("Main thread:Doing other work here....");
Thread.Sleep(10000);
Console.WriteLine("按任意键继续")
}
private static void ComputeBoundOp(Object state){
Consol.WriteLine("In ComputeBoundOp:state={0}",state);
Thread.Sleep(1000);//模拟其他工作1秒钟
}
}
执行上下文
多任务系统往往需要同时执行多道作业。作业数往往大于机器的CPU数,然而一颗CPU同时只能执行一项任务,为了让用户感觉这些任务正在同时进行,操作系统的设计者巧妙地利用了时间片轮转的方式,CPU给每个任务都服务一定的时间,然后把当前任务的状态保存下来,在加载下一任务的状态后,继续服务下一任务。任务的状态保存及再加载,这段过程就叫做上下文切换。
1,每个线程都关联了一个执行上下文的数据结构
2,执行上下文包括:安全设置(压缩栈,Thread的Principal属性和windows身份),宿主设置(System.Treading.HostExcepitonContextManager)以及逻辑调用上下文数据(System.Runtime.Remoting.Messaging.CallContext的LogicalSetData和LogicalGetData)
3,线程执行代码有时候会受到执行上下文设置(安全设置居多)的影响
4,线程执行上下文可流动性。一个线程(初始线程)使用另一个线程(辅助线程)执行任务时,前者上下文应该流向(复制)辅助线程(耗性能).
5,阻止上下文的流动,可提高应用程序性能。
协作式取消
.net Framework提供了一套协作式取消模式,支持显示取消长时间运行的计算限制操作。
System.Thread.CancellationTokenSource对象:
public class CancellationTokenSource : IDisposable
{
//构造函数
public CancellationTokenSource();
//获取是否已请求取消此
System.Threading.CancellationTokenSource
public bool IsCancellationRequested { get; }
//获取与此 System.Threading.CancellationTokenSource 关联的 System.Threading.CancellationToken
public CancellationToken Token;
//传达取消请求。
public void Cancel();
//传达对取消的请求,并指定是否应处理其余回调和可取消操作。
public void Cancel(bool throwOnFirstException);
}
这个对象包含了管理取消有关的所有状态。构造好一个CancellationTokenSource(引 用类型)之后,可以从它的Token属性获得一个或多个CancellationToken(值类型)实 例,并传给你的操作,使那些操作可以取消。以下是CancellationToken值类型最有用 的一些成员:
public struct CancellationToken //一个值类型
{
//获取此标记是否能处于已取消状态,IsCancellationRequested 由非通过Task来调用(invoke)的一个操作调用(call)
public bool IsCancellationRequested { get; }
//如果已请求取消此标记,则引发 System.OperationCanceledException,由通 过Task来调用的操作调用
public void ThrowIfCancellationRequested();
//获取在取消标记时处于有信号状态的 System.Threading.WaitHandle,取消时, WaitHandle会收到信号
public WaitHandle WaitHandle { get; }
//返回空 CancellationToken 值。
public static CancellationToken None
//注册一个将在取消此 System.Threading.CancellationToken 时调用的委托。省略了简单重载版本
public CancellationTokenRegistration Register(Action<object> callback, object state, bool useSynchronizationContext);
//省略了GetHashCode、Equals成员
}
CancellationToken实例是一个轻量级的值类型,它包含单个私有字段:对它的CancellationTokenSource对象的一个引用。在一个计算限制操作的循环中,可以定时调用CancellationToken的IsCancellationRequested属性,了解循环是否应该提前终止,进而终止计算限制的操作。当然,提前终止的好处在于,CPU不再需要把时间浪费在你对其结果已经不感兴趣的一个操作上。
任务
ThreadPool的QueueUserWorkItem 存在限制,没有一个内建机制让你知道什么时候完成,也没有一个机制在操作完成返回一个值。
创建任务:
System.Threading.Tasks创建一个任务
创建Task任务调用的构造器,传递一个Action或者Action<Object>委托
取消任务:
用一个CancellationTokenSource取消一个Task。
一个任务完成时自动启动另一个新任务:
调用ContinuWith()使得一个Task完成时调用新的任务-------- wait方法会导致线程堵塞导致消耗性能
创建子任务:
Task<Int32[]> parent=new Task<Int32[]>(()=>{
var results=new Int32[3];
new Task(()=>results[0])=Sum(10000),TaskCreationOptions.AttacjedToParent).Start();
new Task(()=>results[0])=Sum(20000),TaskCreationOptions.AttacjedToParent).Start();
new Task(()=>results[0])=Sum(30000),TaskCreationOptions.AttacjedToParent).Start();
return results;
});
//父任务及其子任务运行完成后,用一个延续任务显示结果
var cwt=parent.ContinueWith(parentTask=>Array.Foreach(parentTask.Result,console.Writelin));
任务内部揭秘:
1,每个Task对象包含一组构成任务状态的字段:Int32的ID,Task执行状态的Int32,对父任务的一个引用,对Task创建指定TaskScheduler的一个引用,对回掉方法的一个引用,对传值给回调方法的对象一个引用,对一个ExecutionContext的引用,对ManualResetEventSlim对象的引用,以及一些补充状态的一个引用(CancellationToken,ContinueWithTask队形集合,异常Task集合)
2,Task和Task<TResult>实现了IDisposeable接口,完成后调用Dispose
任务工厂:
TaskFactory 和 TaskFactory<TResult>类
任务调度器:
1,TaskScheduler对象负责任务调度,包含两个派生类:线程池任务调度器(thread pool task scheduler)和同步上下文任务调动起(sysnchronization context task scheduler)
默认是前者调度器。后者应用于windows窗体,wpf和silverlight应用程序。
Pareallel的静态For,ForEach 和Invoke方法:
可以帮助提高性能。
计算限制操作的定时器:
System.Treading命名空间定义一个Timer类,可以让线程池线程定时调用搞一个方法。
1,线程池为所有的Timer对象只是用了一个线程。这个线程知道下一个Timer对象什么时候触发
2,当下一个Timer对象到期,线程就会唤醒,内部调用ThreadPool的QueueUserWorkItem(工作项),将工作项添加到线程池的队列中。然后等待回调。
3,如果回调方法执行很长,计时器可能会再次触发 .Timeout.Infinite可让计时器只触发一次。
线程池如何管理线程:
1,System.Threading.ThreadPool类提供:GetMaxTreads,SetMaxThreads,GetMinThreads,SetMinThreads和GetAvailableThreads用于查询和设置线程池的线程数。但是,限制线程池的显成熟,会造成应用程序性能变得更差。
2,ThreadPool.QueueUserWorkItem方法和Timer类将工作项放入全局队列中。工作者进程采用的先入先出(FIFO)
算法将工作项取出,并处理他们。所有工作者线程都竞争一个线程同步所。
3,TaskScheduler调度Task对象:非工作者调用Task对象----Task添加到全局队列----Task添加到调用线程的本地队列------工作线程准好处理工作项----检查本地队列查找一个Task------如果本地队列存在一个Task,就从本地队列移除Task,对工作项进行处理(工作者线程采用后入先出(LIFO)取出本地Task)
缓存线和伪共享
1,CPU在芯片上集成了高速缓存(Cache)使得CPU访问RAM速度非常快。
2,CPU在逻辑上讲所有内存划分为“缓冲行”用于进一步提高性能。一个缓冲行由64个字节构成。