首页 > Python基础教程 >
-
C#教程之线程也疯狂------计算限制的异步操作
前言
异步的限制操作主要作用于其他执行线程,例如规则检查、音频或视频数据转码以及生成图形略缩图,在金融和建筑工程应用程序中,计算限制的操作也是十分普遍的。
CLR线程池
线程池是你的应用程序能使用的线程的一个集合,每个线程池都是由CLR控制的所有AppDomain共享,如果一个进程加载了多个CLR,那么每个CLR都有它自己的线程池,CLR初始化时,线程池中本来没有线程,线程池的内部维护了一个队列请求,应用程序执行一个异步时,就调用某个方法,将一个记录项追加到线程池的队列中,线程池的代码从这个队列中提取记录项,将这个记录项派发给一个线程池线程,如果线程池中没有线程就创建新的线程,当任务执行后,线程并不会销毁,返回线程池,进入空闲状态,等待响应另一个请求,不再消耗额外的性能。
但是当一个线程闲着没事儿一段时间后,线程会自动释放资源,使用线程池的目的就是不用担心线程创建和销毁带来的性能损失。
简单的计算限制操作
ThreadPool类定义的一个方法:
static Boolean QueueUserWorkItem(WaitCallback callBack,object obj);
方法说明:向线程池中添加一个工作项以及可选的状态数据,然后执行此工作项,可向方法传递一个obj参数
代码演示
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Console.WriteLine("程序开始执行"); 6 ThreadPool.QueueUserWorkItem(ComputerSort, 6); 7 Console.WriteLine("模拟其他操作执行5s"); 8 Thread.Sleep(5000); 9 Console.WriteLine("点击Enter退出程序"); 10 Console.ReadLine(); 11 } 12 13 private static void ComputerSort(object obj) 14 { 15 Console.WriteLine("线程池开始执行 参数={0}",obj.ToString()); 16 } 17 }
显示结果
我们发现输出的执行顺序会发生变化,是因为两个方法相互之间在异步上运行的,Windwos调度器决定具体先调度那个线程,如果是多核CPU可能同时调度它们。
执行上下文
每个线程都关联两个一个执行上下文数据结构,执行上下文包括:安全设置(压缩栈、Principal属性、Windows身份)、宿主设置以及逻辑调用上下文数据。
理想情况下,每当一个线程使用另一个线程(辅助线程)执行任务时,前者的执行上下文应该流向后者,确保辅助线程执行的操作都是使用的相同安全设置和宿主设置。
通过阻止执行上下文流动可以提升应用程序的性能,尤其是服务器应用性能提高显著,客户端的效果一般。
事例代码:
1 static void Main(string[] args) 2 { 3 4 //将数据放入Main线程的逻辑上下文中 5 CallContext.LogicalSetData("Name","Tom"); 6 7 ThreadPool.QueueUserWorkItem(p => 8 { 9 Console.WriteLine("Name = {0}",CallContext.LogicalGetData("Name")); 10 }); 11 12 ExecutionContext.SuppressFlow(); 13 14 ThreadPool.QueueUserWorkItem(p => 15 { 16 Console.WriteLine("Name = {0}", CallContext.LogicalGetData("Name")); 17 }); 18 19 ExecutionContext.RestoreFlow(); 20 21 ThreadPool.QueueUserWorkItem(p => 22 { 23 Console.WriteLine("Name = {0}", CallContext.LogicalGetData("Name")); 24 }); 25 26 Console.ReadLine(); 27 28 }
协作式取消和超时
对于长时间运行的计算限制操作,支持取消是一个必要的操作,.Net提供了标准的取消操作模式,无论执行操作的代码,还是试图取消操作的代码,都必须使用下面介绍的类型。
System.Threading.CancellationTokenSource------->这个对象包含了管理取消有关的所有状态,可以从它的Token属性获得一个或者多个CancellationToken实例,
1 static void Main(string[] args) 2 { 3 CancellationTokenSource cts = new CancellationTokenSource(); 4 5 ThreadPool.QueueUserWorkItem(state => Count(cts.Token, 100)); 6 7 Console.WriteLine("输入Enter停止计算"); 8 9 Console.ReadLine(); 10 11 cts.Cancel(); 12 13 Console.ReadLine(); 14 } 15 16 private static void Count(CancellationToken token,int count) 17 { 18 for (int i = 0; i <=count; i++) 19 { 20 if (token.IsCancellationRequested) 21 { 22 Console.WriteLine("接收到取消信号"); 23 break; 24 } 25 Console.WriteLine("i = {0}",i); 26 Thread.Sleep(1000); 27 } 28 }
如果愿意可调用CancellationTokenSource的Register方法登记一个或者多个在取消一个CancellationTokenSource时调用的方法,要向方法传递一个Action<object>委托,一个要通过委托方法传给回调,以及一个Boolean值,该值指明是否要使用调用线程的SynchroinzationContent来调用委托。
1 cts.Token.Register(() => 2 { 3 Console.WriteLine("接收到取消信号后,开始调用回调函数"); 4 });
在很多情况下,我们需要在过一段时间之后才取消操作,例如服务器应用程序可能会根据客户端的请求进行计算,但必须在两秒内响应,无论是否完成必须结束此次会话。在.NET 4.5中,CacncellationTokenSource提送了一个CancelAfter的方法
1 public void CancelAfter(int millisecondsDelay)
本章节的内容就讲解到这里,下节我们将会继续 线程也疯狂-----异步编程。