一、线程基础
(1)作用:为设计和实现可伸缩的,可响应和可靠的应用程序和组件,线程是必须的核心技术。
二、CLR线程池
(1)CLR线程池线程池的引入:
创建和销毁线程是一个昂贵的操作,需要大量的时间
太多的线程会浪费内存资源,线程的上下文切换还有损性能
(2)CLR线程池是什么
想像成线程的集合,线程池可以容纳少量线程,从而避免浪费资源,也可以容纳功能多的线程,以利用多处理器,超线程出来器和多核处理器。如果应用程序发出请求
的速度超过了线程池的处理速度,就会创建额外的线程。当一个线程池线程很闲,线程会自己醒来终止自己以释放资源。
(3)每个CLR一个线程池,这个线程池由CLR控制的所有AppDomain共享。如果一个进程加载多个CLR,每个CLR都有自己的线程池.
(4)线程池线程划分:
工作者线程:异步的计算限制操作
I/O线程:I/O限制的异步操作
(5)线程池如何管理线程(工作者线程和I/O线程)
将线程池看成一个黑盒,是一种常规用途的线程调度技术,强烈信任它。强烈建议不要调用限制线程池的线程数,一般只会造成应用程序的性能变得越差,而不会变得更好。
三、计算限制的异步操作
(一)概念:使用其他线程执行它
(二)举例:编译代码,拼写检查,语法检查,电子表格重计算,音频或视频数据转码以及生成图像的缩略图。金融行业和工程行业。
(三)计算限制的异步操作解决办法
(1)线程池ThredPool实现
(2)任务Task实现
线程池ThreadPool的QueueUserWorkItem方法发起一异步的,受计算机限制的操作室非常简单的。然而这个技术存在很多限制。最大的问难是没有一个内建的机制让你
知道操作什么时候完成,也没有一个机制在操作完成的时候获得一个返回值。为了克服这些限制。Microsoft引入了任务的概念
(3)执行定时的计算限制操作
System.Threading命名空间中一个Timer类.可用它让一个线程池定时调用一个方法。相当于告诉线程池在某一个时间(具体由你指定)回调你的方法。
四、I/O限制的异步操作
(二)异步方式执行I/O操作的好处
一) 可以将线程数控制在少数几个
(1)资源使用率的降低,并减少了上下问的切换,
(2)线程越少垃圾回收器运行得速度越快(因为每一次垃圾回收,CLR必须挂起进程中的所有线程)
(3)线程越少,栈的数量越少,垃圾回收器的素对变得越快。
(4)线程越少调试更容易(因为调试过程中,一旦遇到断点,会挂起调试程序中的所有线程,继续执行调试的应用程序,Windows必须恢复它的所有线程)
二)如果执行多个异步I/O操作获得所有结果的时间是表现最差的那个操作所需时间(同步操作是所有操作时间之和)
三)对于GUI应用程序:应用程序的用户界面不会挂起,会在最终用户面前一直保持可响应性(Silverlight没有提供同步的I/O操作,执行同步I/O操作的线程可能阻塞以等待Web服务器响应会导致整个浏览器冻结,用户不能切换到另一个标签页)
五、异步编程模型
异步操作是构建高性能、可伸缩应用程序的关键,它允许用非常少的线程执行许多操作。和线程池配合,异步操作允许你利用机器中的所有CPU。
异步模型允许多得多的并发客户端,允许使用少的少的资源,能够更快的处理客户端(因为减少了上文切换)提高了垃圾回收速度,并增强了调试性能。
一)CLR异步编程模型APM(Asynchronous Programming Model)
1)概念及原理
以BeginXxx调用结束,以一个EndXxx调用开始,在EndXxx和BeginXxx方法之间,只执行计算限制的操纵;I/O操作在这些方法的“边界”处执行,所以线程永远不会阻塞。在每个方法之后,线程都回到线程池中,以便在哪里处理传入的客户端请求或者传入的网络响应。如果线程很忙,它会自动创建多个线程处理工作负荷。服务器能根据工作负荷和机器中的CPU数而自动伸缩。
2)举例说明
这种方式就是提供两个方法实现异步编程:比如System.IO.Stream的Read方法:
public int Read(byte[] buffer,int offset,int count);
它还提供了两个方法实现异步读取:
public IAsyncResult BeginRead(byte[] buffer, int offset,int count,AsyncCallback callback);
public int EndRead(IAsyncResult asyncResult);
其中callback具有AsyncCallBack委托类型
public delegate void AsyncCallback(IAsyncResult ar)
3)APM的缺点
(1)必须将代码分解成多个回调方法
(2)与传统方法的调用方 式相比,异步调用时的中间数据不能存放在线程栈上,方法之间的也不能简单地通过参数传递的方式来共享数据。此外,传统方法调用中可使用的 try…catch…finally,using等关键字都无法跨越方法边界,因此异步编程在处理异常,保护资源等方面也需要花更大的精力才行。如果一不 小心,轻则造成资源泄露,重则使整个应用程序崩溃。
(3)很难实现多个并发操作协作进行,取消和超时,以及将工作封送给GUI线程以更新控件
辅助类库让异步更简单Jeffrey Richter的AsyncEnumerator。允许使用同步编程模型来执行异步操作
4)APM和计算限制的操作
(1)委托符合APM模型,用委托实现计算限制操作(异步回调实现)
(2)原理:委托的BeginInvoke方法是在内部调用ThreadPool的QueueUserWorkItem将计算限制的操作添加到CLR的线程池队列中。最后BeginInvoke将IAsysncResult对象返回给它的调用者。
(3)优缺点
通过委托的BeginInvoke方法,任何方法都可以异步调用,但这样做的实时是在使用一个线程,所以效率会有一定损失。
/// <summary> /// 为了保证程序UI的可响应性,或者为了利用计算机中的其他CPU,希望异步执行Sum方法 /// (一)委托符合APM模型,用委托实现计算限制操作(异步回调实现) /// Fun<T,TResut>委托 /// public delegate TResult Func<T,TResult>(T arg);中包含了 /// public IAsncResult BeginInvoke(T arg,AsyncCallback callback,Object object) /// public TResult EndInvoke(IAsyncResult result) /// 其实任何委托都包含了BeginInvoke,和EndInvoke这个是符合APM编程模型的 /// 所以用委托实现计算限制的操作太简单了 /// (二)原理 /// 委托的BeginInvoke方法是在内部调用ThreadPool的QueueUserWorkItem将计算限制的操作添加到CLR的线程池队列中。 /// 最后BeginInvoke将IAsysncResult对象返回给它的调用者。 /// </summary> private static void APMSum() { //初始化一个委托变量,让它引用想要的异步调用方法 Func<UInt64, UInt64> sumDelegate=Sum; //使用一个线程池线程调用方法 sumDelegate.BeginInvoke(1000000000,SumIsDone,sumDelegate); //在这里可以执行其他的代码 //处于演示的目的我们将主线程挂起 Console.WriteLine(); } private static void SumIsDone(IAsyncResult ar) { //从IAsyncResult对象中提取sumDelegate(state) var sumDelegate=(Func<UInt64,UInt64>) ar.AsyncState; try { //获取结果并显示它 Console.WriteLine("Sum's result:" + sumDelegate.EndInvoke(ar)); } catch(OverflowException) { Console.WriteLine("Sum's restult is too large to caculate"); } } private static UInt64 Sum(UInt64 n) { UInt64 sum = 0; for (UInt64 i = 1; i <= n; i++) { checked { //在此使用checked代码,用一个UInt64装不下 //sum抛出一个OverflowException异常 sum += i; } } return sum; }
(4)APM的注意事项
(5)将IASyncResult转为Task
二)基于事件的编程模型EAP(Event-based Asynchronous Pattern)
(1)优点:它同Microsoft Visual Studio UI设计器进行了很好的集成
支持EAP的类自动将应用程序映射到他得线程处理模型
EAP类在内部使用SynchronizationContext类
提供了取消和进度报告功能
(2)FCL中有17个类型实现了EAP模型
列如:System.ComponetModel.Component的派生类:System.ComponetModel.BackgroundWorker System.Net.WebClient
System.Object的派生类:System.Net.Mail.SmtpClient
而这17类中只有BackgroundWorker类用于执行异步的计算限制的工作,如果用于I/O限制会阻塞一个线程。
(3)EAP执行I/O限制操作举例
private void button4_Click(object sender, RoutedEventArgs e) { //System.Net.WebClient类支持基于事件的异步编程模型(EAP) WebClient wc = new WebClient(); wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted); //开始异步操作(这个类似于调用一个BeginXxx方法) wc.DownloadStringAsync(new Uri("http://www.baidu.com")); } void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { if (e.Error != null) { MessageBox.Show(e.Error.Message); } else { MessageBox.Show(e.Result); } }
(4)EAP(BackgroundWorker类用于执行异步的计算限制的工作)
基于事件的异步模式
五、应用程序及其线程处理模型
(1)控制台应用程序和Windos服务
没有引入任何种类的线程处理模型
(2)GUI应用程序(包括Windows窗体、WPF和Silverlight)
引入了一个线程处理模型:创建窗口的线程是唯一能对那个窗口进行更新的线程。
在GUI线程中经常需要生产一个异步操作,使GUI线程不至于阻塞并停止响应用户输入(鼠标、键盘事件).由于异步操作是一个线程池完成的,而线程池不能更新UI。因此线程池需要用某种方式让GUI线程更新UI.
这种方式就是Invoke(WinForm),Dispatcher.Invoke(WPF)
可以参看如下链接
(3)Asp.Net Web窗体和XMl Web服务
类似于控制台应用程序。允许线程做它爱做的任何事情
六、.net4.0新秀任务Task对I/O限制,计算限制通吃(线程池也有这个能力)
七、项目中碰到的问题
(1)定时调用WebService
(2)切换Tab页调用不同webservice(服务数据每一分钟有更新)将获取到到不同的数据显示到TabItem页面上(就是要每次切到Tab页上要重新调用webservice).过程中切换过快的问题。
解决办法:1)在vs中生成同步的WebService服务,在调用时需要使用线程或者异步方式调用服务
2)在vs中生成异步的Webservice服务,在调用时需要在异步回调的方法中操作UI控件。也可以手动编写一部服务。将你服务实现写成2个方法(BeginXxx,EndXxx并加上[WebMethod]特性标记符。
(3)拖动滚动条分批加载数据呈现(需要用异步调用webservice)
八、代码下载