欢迎来到天天文库
浏览记录
ID:48128217
大小:219.42 KB
页数:10页
时间:2020-01-21
《异步调用机制及实现方法.doc》由会员上传分享,免费在线阅读,更多相关内容在行业资料-天天文库。
1、这篇文章将介绍异步调用的实现机制及如何调用异步方法。大多数.NET开发者在经过delegate、Thread、AsynchronousInvocation之后,通常都会对以上概念产生混淆及误用。实际上,以上概念是.NET2.0版本中对并行编程的核心支持,基于概念上的错误认识有可能导致在实际的编程中,无法利用异步调用的特性优化我们的程序,例如大数据量加载引起的窗体”假死”。事实上这并不是一个困难的问题,该文将以一种逐层深入、抽丝剥茧的方式逐渐深入到异步编程的学习中。同步与异步 大多数人并不喜欢阅读大量的文字说明,而喜欢直接阅读代码,因此,我
2、们在下文中将主要以代码的形式阐述同步与异步的调用。同步方法调用 假设我们有一个函数,它的功能是将当前线程挂起3秒钟。staticvoidSleep(){Thread.Sleep(3000);} 通常,当你的程序在调用Sleep后,它将等待3秒钟的时间,在这3秒钟时间内,你不能做任何其他操作。3秒之后,控制权被交回给调用线程(通常也就是你的主线程,即WinForm程序的UI线程)。这种类型的调用称为同步,本次调用顺序如下: ●调用Sleep(); ●Sleep()执行中; ●Sleep()执行完毕,控制权归还调用线程。 我们再次调
3、用Sleep()函数,不同的是,我们要基于委托来完成这次调用。一般为了将函数绑定在委托中,我们要定义与函数返回类型、参数值完全一致的委托,这稍有点麻烦。但.NET内部已经为我们定义好了一些委托,例如MethodInvoker,这是一种无返回值、无参数的委托签名,这相当于你自定义了一种委托:publicdelegatevoidSimpleHandler(); 执行以下代码:MethodInvokerinvoker=newMethodInvoker(Sleep);invoker.Invoke(); 我们使用了委托,但依然是同步的方式。主线程
4、仍然要等待3秒的挂起,然后得到响应。注意:Delegate.Invoke是同步方式的。异步方法调用 如何在调用Sleep()方法的同时,使主线程可以不必等待Sleep()的完成,一直能够得到相应呢?这很重要,它意味着在函数执行的同时,主线程依然是非阻塞状态。在后台服务类型的程序中,非阻塞的状态意味着该应用服务可以在等待一项任务的同时去接受另一项任务;在传统的WinForm程序中,意味着主线程(即UI线程)依然可以对用户的操作得到响应,避免了”假死”。我们继续调用Sleep()函数,但这次要引入BeginInvoke。MethodInvok
5、erinvoker=newMethodInvoker(Sleep);invoker.BeginInvoke(null,null); ●注意BeginInvoke这行代码,它会执行委托所调用的函数体。同时,调用BeginInvoke方法的线程(以下简称为调用线程)会立即得到响应,而不必等待Sleep()函数的完成。 ●以上代码是异步的,调用线程完全可以在调用函数的同时处理其他工作,但是不足的是我们仍然不知道对于Sleep()函数的调用何时会结束,这是下文将要解决的问题。 ●eginInvoke可以以异步的方式完全取代Invoke,我们也
6、不必担心函数包含参数的情况,下文介绍传值问题。注意:Delegate.BeginInvoke是异步方式的。如果你要执行一项任务,但并不关心它何时完成,我们就可以使用BeginInvoke,它不会带来调用线程的阻塞。对于异步调用,.NET内部究竟做了什么? 一旦你使用.NET完成了一次异步调用,它都需要一个线程来处理异步工作内容(以下简称异步线程),异步线程不可能是当前的调用线程,因为那样仍然会造成调用线程的阻塞,与同步无异。事实上,.NET会将所有的异步请求队列加入线程池,以线程池内的线程处理所有的异步请求。对于线程池似乎不必了解的过于深
7、入,但我们仍需要关注以下几点内容: ●Sleep()的异步调用会在一个单独的线程内执行,这个线程来自于.NET线程池。 ●.NET线程池默认包含25个线程,你可以改变这个值的上限,每次异步调用都会使用其中某个线程执行,但我们并不能控制具体使用哪一个线程。 ●线程池具备最大线程数目上限,一旦所有的线程都处于忙碌状态,那么新的异步调用将会被置于等待队列,直到线程池产生了新的可用线程,因此对于大量异步请求,我们有必要关注请求数量,否则可能造成性能上的影响。简单了解线程池 为了暴露线程池的上限,我们修改Sleep()函数,将线程挂起的时间延
8、长至30s。在代码的运行输出结果中,我们需要关注以下内容: ●线程池内的可用线程数量。 ●异步线程是否来自于线程池。 ●线程托管ID值。 上文已经提到,.NET线程池默认
此文档下载收益归作者所有