多线程编程之计算限制型异步操作
1. CLR线程池简介
1.1 CLR为什么支持线程池
1.2 线程池ThreadPool管理线程
2. 线程执行上下文
2.1 线程执行上下文简介
2.2 一个简单示例
3. 线程池常见应用情景示例
3.1 将一个线程添加至线程池中(向线程传递参数)
3.2 协作式取消
4. Task简介
5. Task编程基础
6. 定时器Timer
<1>. CLR线程池简介
1.1 CLR为什么支持线程池?
上一篇中讲到如果在一个应用程序中启动了多个线程的话,显然是会影响到程序的运行效率,一种很直观的想法是:如果一个线程完成了任务,我们并不在内存中销毁这个线程的实例,而是将这个线程进入空闲状态,如果又来了一个请求,可以不用另外创建新线程,这样显然能够带来性能上的提升,在.net中,CLR为我们实现了线程池,它可以看作是线程的一个集合。每个CLR都存在一个全局的线程池,供所有的AppDomain共享。
1.2 线程池ThreadPool管理线程
System.Threading.ThreadPool中提供了查询和设置线程池性质的的静态方法。
public static bool SetMaxThreads(int workerThreads, int completionPortThreads); // 设置线程池中线程数量的最大值,但是永远不要折磨做,容易发生死锁等情况
public static bool SetMinThreads(int workerThreads, int completionPortThreads); // 设置线程池中线程数量的最小值
public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads); // 返回线程池支持的最大线程数和当前活动的线程的差值
<2>. 线程执行的上下文及其流转
2.1 线程执行上下文简介
线程的执行上下文包含安全设置,宿主设置,线程上下文数据(System.Runtime.Remoting.Messaging.CallContext的LogicalSetData方法)等信息。当一个线程使用另外的线程执行任务时,前者的线程执行上下文将流转至后者的线程执行上下文中。在c#中类型System.Threading.ExecutionContext来控制线程执行上下文的流转:
public static AsyncFlowControl SuppressFlow(); // 阻止( Suppress本意是抑制)线程执行上下文的流转
public static bool IsFlowSuppressed(); // 判断当先线程执行上下文是否被阻止
2.2 一个简单示例
}
通过上面的建立示例,我们能够明确,
1. 如何向线程池中添加工作线程:
通过调用QueueUserWorkItem,该静态方法存在两个重载方法:
public static bool QueueUserWorkItem(WaitCallback callBack); // 不需要传递参数
public static bool QueueUserWorkItem(WaitCallback callBack, object state); // 需要向线程传递参数
2. 通过函数SuppressFlown能够阻止线程上下文的流转,通过RestoreFlow能够恢复线程上下文
3. 通过Thread.Sleep函数能够阻塞调用Thread.Sleep的线程(这里是指Main线程)
<3>. 线程池常见应用场景示例
3.1 将一个线程添加至线程池中(向线程中传递参数)
}
3.2 协作式取消
之所以称之为“协作式取消”,是讲一个线程T如果想要获得被取消的能力的话,那么T需要接受一个类型为CancellationToken参数(该类型存在于.net 4.0中,3.5中不存在),在T中通过查询CancellationToken的IsCancellationRequested属性来判断是否已经被取消。下面结合这个场景来分析这个过程中使用到的类CancellationTokenSource和CancellationToken:
{
public CancellationTokenSource(); // 默认构造函数
public bool IsCancellationRequested { get; } // 查询线程是否已经被取消
public CancellationToken Token { get; } // 构造好了CancellationTokenSource通过该方法得到CancellationToken实例,然后传递给需要实现协作式取消的线程T
public void Cancel(); // 通过该方法取消T线程
// 取消线程T的重载版本,可以通过CancellationTokenSource的CancellationToken属性的Register注册委托,在Cancel方法调用后执行。如
// 果throwOnFirstException为true的话,那么在注册的方法中的任何一个方法抛出未处理异常,Cancel将抛出该异常 ,同时阻止其他回调方法
// 的调用,如果throwOnFirstException为false的话,如果在任何的回调方法中抛出异常不会影响到其他回调方法的调用,未处理的异常将添加
// 至AggregateException中
public void Cancel(bool throwOnFirstException);
public void Dispose(); // 实现IDisposable接口
// 连接另外一组 CancellationToken来创建CancellationTokenSource示例,例如如果通过var cts = CreateLinkedTokenSource(token1, token2)
// 那么如果token1或者token2取消,cts将被取消
public static CancellationTokenSource CreateLinkedTokenSource(params CancellationToken[] tokens);
// 重载版本
public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2);
}
public struct CancellationToken
{
public bool IsCancellationRequested { get; } // 得到是否已经存在对于该CancellationToken的取消操作
public static CancellationToken None { get; } // 如果一个工作进程不想被取消的话,可以使用该静态字段
// 下面的几个方法将向 CancellationToken中注册委托时间,如果CancellationToken被取消,将调用这些方法
public CancellationTokenRegistration Register(Action callback);
public CancellationTokenRegistration Register(Action<object> callback, object state);
// 该方法主要解决在winform,wpf等中,工作线程如何更新ui线程
public CancellationTokenRegistration Register(Action callback, bool useSynchronizationContext);
....
}
这个示例演示了如何使用CancellationTokenSource,并向其中注册回调方法,如何管理异常等特性:
}
下面的示例将演示如何实现一个链接的CancellationTokenSource
}
下一篇继续...
作者:许强1. 本博客中的文章均是个人在学习和项目开发中总结。其中难免存在不足之处 ,欢迎留言指正。 2. 本文版权归作者和博客园共有,转载时,请保留本文链接。