该笔记记录的是Jiffrey Richter《CLR via C#》 3rd Edition的chapter 26 Compute-Bound Asynchronous Operations的前四节内容。
Jiffrey Richter《CLR via C#》3rd Edition下载地址:这里 或者 wowebook中找。
在这里你能过找到一些简单的执行异步操作的例子。
所谓线程池,通俗地讲,就是一大堆线程的集合,可以给你的应用程序所使用。但是,线程池刚初始化时,是没有一个线程的。主要是因为,太多的线程会消耗内存资源和降低性能。当你的application需要进行异步计算,某些方法进入线程池队列,线程池创建第一个线程。由于创建线程有个performance hit,所以当线程池中德线程完成任务后,不会被destroy。相反地,返回给线程池等待下个调用,这样就没有产生performance hit了。
上述情况是只有一个线程或少量线程请求调用,当你的application产生大量请求时,线程池首先会试着用一个线程来完成。但当application请求适度大于线程的处理速度,这时另外的线程就创建了。因此,线程池是会创建尽可能少得线程来处理application的请求,保障了performance。
线程池把所有请求处理完后,也木有产生新的请求。在一个周期后,线程会自杀。这样就不会浪费内存资源。
所以,线程池会自我权衡,建立少量线程来减少损耗;建立更多的线程来提高效率。
线程池中的线程分为:worker thread 和I/O thread。
以下是使用线程池的方法(在threadPool class中定义):
Static Boolean QueueUserWorkItem(WaitCallback callBack);
Static Boolean QueueUserWorkItem(WaitCallback callBack, Object state);
Sysytem.Threading.WaitCallback委托类定义如下:
delegate void WaitCallback(Object state);
Coding一个:
static void Main(string[] args){Console.WriteLine("Main thread.");
Thread.Sleep(1000);ThreadPool.QueueUserWorkItem(state =>{Console.WriteLine("Other thread");
Thread.Sleep(1000);});Console.WriteLine("Main thread is doing other work");
Thread.Sleep(1000);Console.WriteLine("Hit <Enter> to end this program...");
Console.ReadKey();}
Run 输出:
Main thread.
Main thread is doing other work
Other thread也可能输出:
Main thread.
Main thread is doing other work
Other thread
每个线程都有一个执行上下文(Execution Contexts)。执行上下文包含security setting(compressed stack, Thread`s Principal property and Windows identity)、host setting(详细见System.Threading.)HostExecutionContextManager) 和 logical call context data(详细见System.Runtime.Remoting.Messaging.CallContext的方法——LogicalSetData和LogicalGetData)。
当前线程开启一个线程(helper thread)时,当前的执行上下文会自动流到helper thread,这样保障了security setting和host setting的一致性。执行上下文的复制到新线程是要性能损耗的。在System.Thread中有一个类叫ExecutionContext可以控制执行上下文的流动。
ExecutionContext类似于:
public sealed class ExecutionContext : IDisposable, ISerializable
{
[SecurityCritical]
public static AsyncFlowControl SuppressFlow();
public static void RestoreFlow();
public static Boolean IsFlowSuppressed();
}你能够使用ExecutionContext 控制执行上下文流动,从而提高应用程序的性能,在服务程序中能得到显著地提高,而在客户端程序不会很有效。当然,为了不让新的线程使用执行上下文中的数据时使用该类实现。
附上一个简单的例子:
CallContext.LogicalSetData("Name", "LiLin");ThreadPool.QueueUserWorkItem(state => Console.WriteLine("My name is {0}", CallContext.LogicalGetData("Name")));ExecutionContext.SuppressFlow();ThreadPool.QueueUserWorkItem(state => Console.WriteLine("My name is {0}", CallContext.LogicalGetData("Name")));ExecutionContext.RestoreFlow();
输出My name is LiLin
My name is
MicroSoft .NET Framework为取消操作提供一标准模式,叫做协作式。换句话说,你可以明确规定你的操作可以被取消,这对于耗时的肌酸异步限制操作很有用。
实现协作式取消,会用到System.Threading.CancellationTokenSource这个类,这个类的定义类似于:
public sealed class CancellationTokenSource : IDisposable
{
public CancellationTokenSource ();
public void Dispose();
public Boolean IsCancellationRequested { get ; }
public CancellationToken Token { get ; }
public void Canel();
public void Cancel( Boolean throwOnFirstException);
}CancellationTokenSource 是引用类型,而CancellationToken 是值类型,定义类似于:
public struct CancellationToken
{
public Boolean isCanllationRequested { get; }
public void ThrowIfCancellationRequested();
public WaitHandle WaitHandle { get; }
public static CancellationToken None { get; };
public Boolean CanBeCanceled { get ; }
public CancellationTokenRegistration Register( Action<Object> callback, Object state, Boolean useSynchronizationContecxt);
}一个简单的例子:
CancellationTokenSource cts = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(o => LongTimeCalculate(cts.Token, 20));Console.WriteLine("Press <ENTER> to cancel the calculate.");
Console.ReadKey();cts.Cancel();
Press <ENTER> to cancel the calculate.
Calculate is cancelled!
Done!
假如你不想让这个calculate方法被取消,可以使用CancellationToken类中的静态None属性。
如果,你想通过一个CancellationTokenSource 协作式取消一个或多个方法,可以使用CancellationToken`s Register方法。关于这个方法,你可以传入一个Action<Object>的委托,一个state值,一个布尔值指定是否在调用该委托时使用同步执行上下文(SynchronizationContext)。
CancellationToken`s Register方法返回一个CancellationTokenRegistration:
public struct CancellationTokenRegistration : IEquatable<CancellationTokenRegistration>, IDisposable
{
public void Dispose();
…
}因此,可以调用Dispose移除一个register方法。
下面简单的使用CancellationToken`s Register方法:
CancellationTokenSource cts = new CancellationTokenSource();
cts.Token.Register(() => Console.WriteLine("Method 1"));
cts.Token.Register(() => Console.WriteLine("Method 2"));
cts.Cancel();
输出:
Method 2
Method 1