博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
异步编程模式(三):等待异步调用的完成
阅读量:5907 次
发布时间:2019-06-19

本文共 6475 字,大约阅读时间需要 21 分钟。

当程序启动一个异步调用之后,调用者线程必须有一种方法能知道此调用的执行情况,并且在这一调用执行完毕之后,取回执行结果。

可以有以下二种方法:

一、使用轮询

现在我们来改造上一节的示例程序。我们可以在程序执行异步调用的过程中,让计算机每隔一段时间向控制台输出一个小点,告诉用户搜索工作正在进行中,

从而可以大大改善程序的用户友好性。

示例程序:

namespace AsyncCalculateFolderSize2 {
class Program {
//计算指定文件夹的总容量 private static long CalculateFolderSize(string FolderName) {
if (Directory.Exists(FolderName) == false) {
throw new DirectoryNotFoundException("文件夹不存在"); } DirectoryInfo RootDir = new DirectoryInfo(FolderName); //获取所有的子文件夹 DirectoryInfo[] ChildDirs = RootDir.GetDirectories(); //获取当前文件夹中的所有文件 FileInfo[] files = RootDir.GetFiles(); long totalSize = 0; //累加每个文件的大小 foreach (FileInfo file in files) {
totalSize += file.Length; } //对每个文件夹执行同样的计算过程:累加其下每个文件的大小 //这是通过递归调用实现的 foreach (DirectoryInfo dir in ChildDirs) {
totalSize += CalculateFolderSize(dir.FullName); } //返回文件夹的总容量 return totalSize; } //定义一个委托 public delegate long CalculateFolderSizeDelegate(string FolderName); static void Main(string[] args) {
//定义一个委托变量引用静态方法CalculateFolderSize CalculateFolderSizeDelegate d = CalculateFolderSize; Console.WriteLine("请输入文件夹名称(例如:C:\\Windows):"); string FolderName = Console.ReadLine(); //通过委托异步调用静态方法CalculateFolderSize IAsyncResult ret = d.BeginInvoke(FolderName, null, null); Console.Write ("正在计算中,请耐心等待"); while (ret.IsCompleted == false) {
Console.Write("."); //每隔2秒检查一次 System.Threading.Thread.Sleep(2000); } //阻塞,等到调用完成,取出结果 long size = d.EndInvoke(ret); Console.WriteLine("\n计算完成。文件夹{0}的容量为:{1}字节\n", FolderName, size); Console.ReadKey(); } } }

上面的程序中,在启动异步调用后,定期查询异步调用的状态,如果还没有完成,就输出一个小点。

IAsyncResult接口中有一个IsCompleted字段,可以用于检查异步调用是否完成。

 

 

二、使用等待句柄

上面的代码使用轮询 IAsyncResult.IsCompleted属性值的方式不断询问异步调用是否完成,还可以使用 IAsyncResult提供的另一个属性AsyncWaitHandle实现

同样的目的。 AsyncWaitHanle 是一个等待句柄对象,它定义了一系列重载的 WaitOne方法,我们将使用后个如下所示:

public virtual bool WaitOne(int millisecondsTimeout);

 

当调用以上形式的WaitOne方法时,调用者线程将在由方法参数millisecondsTimeout指定的时间段内等待“等待句柄”对象的状态转为Signaled,此时

WaitOne方法返回true;如果超时,返回false.

示例代码如下:

namespace AsyncCalculateFolderSize3 {
class Program {
//计算指定文件夹的总容量 private static long CalculateFolderSize(string FolderName) {
if (Directory.Exists(FolderName) == false) {
throw new DirectoryNotFoundException("文件夹不存在"); } DirectoryInfo RootDir = new DirectoryInfo(FolderName); //获取所有的子文件夹 DirectoryInfo[] ChildDirs = RootDir.GetDirectories(); //获取当前文件夹中的所有文件 FileInfo[] files = RootDir.GetFiles(); long totalSize = 0; //累加每个文件的大小 foreach (FileInfo file in files) {
totalSize += file.Length; } //对每个文件夹执行同样的计算过程:累加其下每个文件的大小 //这是通过递归调用实现的 foreach (DirectoryInfo dir in ChildDirs) {
totalSize += CalculateFolderSize(dir.FullName); } //返回文件夹的总容量 return totalSize; } //定义一个委托 public delegate long CalculateFolderSizeDelegate(string FolderName); static void Main(string[] args) {
//定义一个委托变量引用静态方法CalculateFolderSize CalculateFolderSizeDelegate d = CalculateFolderSize; Console.WriteLine("请输入文件夹名称(例如:C:\\Windows):"); string FolderName = Console.ReadLine(); //通过委托异步调用静态方法CalculateFolderSize IAsyncResult ret = d.BeginInvoke(FolderName, null, null); Console.Write("正在计算中,请耐心等待"); while(!ret.AsyncWaitHandle.WaitOne(2000)) {
//等待2秒钟,输出一个“.” Console.Write("."); } //阻塞,等到调用完成,取出结果 long size = d.EndInvoke(ret); Console.WriteLine("\n计算完成。文件夹{0}的容量为:{1}字节\n", FolderName, size); Console.ReadKey(); } } }

 

 

三、异步回调

前面两个示例使用轮询的方式不断询问异步调用是否完成,这无疑会在循环等待上浪费不少CPU时间。能不能让异步调用的方法在结束时自动调用一个方法,并在

这个方法中显示处理结果?

使用异步回调可以满足这个要求。

BeginInvoke 方法定义中的最后两个参数是 "AsyncCallback callback" 和 "object asyncState",这两个参数就是用于异步调用的。

以下是示例代码:

namespace AsyncCalculateFolderSize4 {
class Program {
//计算指定文件夹的总容量 private static long CalculateFolderSize(string FolderName) {
if (Directory.Exists(FolderName) == false) {
throw new DirectoryNotFoundException("文件夹不存在"); } DirectoryInfo RootDir = new DirectoryInfo(FolderName); //获取所有的子文件夹 DirectoryInfo[] ChildDirs = RootDir.GetDirectories(); //获取当前文件夹中的所有文件 FileInfo[] files = RootDir.GetFiles(); long totalSize = 0; //累加每个文件的大小 foreach (FileInfo file in files) {
totalSize += file.Length; } //对每个文件夹执行同样的计算过程:累加其下每个文件的大小 //这是通过递归调用实现的 foreach (DirectoryInfo dir in ChildDirs) {
totalSize += CalculateFolderSize(dir.FullName); } //返回文件夹的总容量 return totalSize; } public delegate long CalculateFolderSizeDelegate(string FolderName); private static CalculateFolderSizeDelegate d=CalculateFolderSize; //用于回调的函数 public static void ShowFolderSize(IAsyncResult result) {
long size = d.EndInvoke(result); Console.WriteLine("\n文件夹{0}的容量为:{1}字节\n", (String)result.AsyncState, size); } static void Main(string[] args) {
string FolderName; while (true) {
Console.WriteLine("请输入文件夹名称(例如:C:\\Windows),输入quit结束程序"); FolderName = Console.ReadLine(); if (FolderName == "quit") break; d.BeginInvoke(FolderName, ShowFolderSize, FolderName); } } } }

注意:

1、调用BeginInvoke方法的那句代码。BeginInvoke方法的第2个参数指定当异步调用结束时回调 ShowFolderSize方法,第3个参数asyncState被填入了要计算的文件夹

名字,此值被BeginInvoke方法包装到自动创建的一个 IAsyncResult类型的对象中,并作为方法实参自动传送给回调方法(即本示例中回调方法ShowFolderSize的参数result),

回调方法通过这一实参的AsyncState字段获取其值。

2、回调方法的返回值类型是void,只能有一个 IAsyncResult类型的参数result,并且要在方法体中调用 EndInvoke 方法以取回方法的执行结果,另外,result参数的AsyncState

属性包含了外界传入的参数信息(本例为文件夹名)。

 

这个程序现在可以连续输入多个文件夹名称,计算机在后台分别计算,完成后就在控制台窗口中输出结果。

这里还有一个注意点:先输入的文件夹(先执行的任务)并不一定先完成,可能后面执行的任务由于工作量小反而先执行完。

 

 

这里可以得出一个结论:如果需要将一些额外的信息传送给回调方法,就将其放入 BeginInvoke 方法的第3个参数asyncSate中。注意到

这个参数的类型为object ,所以可以放置任意类型的数据。 (在后面将会说到这个参数,其实类库中大多数异步的方法都有这个参数)!

 

转载于:https://www.cnblogs.com/eagle1986/archive/2012/01/19/2327592.html

你可能感兴趣的文章
mariaDB安装完成后设置root密码等初始化操作
查看>>
STL入门
查看>>
ThreadLocal对象使用过程中容易陷入的坑
查看>>
零元学Expression Blend 4 - Chapter 36 来玩捉迷藏吧!!!看看ScrollBar的Disabled与Hidden之差异...
查看>>
Oracle的sqlnet.ora文件配置
查看>>
【Django】 rest-framework和RestfulAPI的设计
查看>>
nuget.org 无法加载源 https://api.nuget.org/v3/index.json 的服务索引
查看>>
CentOS 7 搭建基于携程Apollo(阿波罗)配置中心单机模式
查看>>
PHP与Excel 笔记
查看>>
js使用正则表达式验证身份证格式
查看>>
捋一捋js面向对象的继承问题
查看>>
hbase分布式集群搭建
查看>>
ASP.NET Core 2.0 : 六. 举个例子来聊聊它的依赖注入
查看>>
桥接模式-pattern系列
查看>>
centos7环境安装ElasticSearch
查看>>
VidLoc: A Deep Spatio-Temporal Model for 6-DoF Video-Clip Relocalization
查看>>
SpringMVC RESTful风格URL处理带点的参数
查看>>
面试DB优化
查看>>
ORA-03137: TTC 协议内部错误: [12333] [4] [49] [51] [] [] [] []
查看>>
【laravel5.4】自定义404、503等页面
查看>>