异步编程模式(三):等待异步调用的完成
2012-01-19 19:50
423 查看
当程序启动一个异步调用之后,调用者线程必须有一种方法能知道此调用的执行情况,并且在这一调用执行完毕之后,取回执行结果。
可以有以下二种方法:
一、使用轮询
现在我们来改造上一节的示例程序。我们可以在程序执行异步调用的过程中,让计算机每隔一段时间向控制台输出一个小点,告诉用户搜索工作正在进行中,
从而可以大大改善程序的用户友好性。
示例程序:
上面的程序中,在启动异步调用后,定期查询异步调用的状态,如果还没有完成,就输出一个小点。
IAsyncResult接口中有一个IsCompleted字段,可以用于检查异步调用是否完成。
二、使用等待句柄
上面的代码使用轮询 IAsyncResult.IsCompleted属性值的方式不断询问异步调用是否完成,还可以使用 IAsyncResult提供的另一个属性AsyncWaitHandle实现
同样的目的。 AsyncWaitHanle 是一个等待句柄对象,它定义了一系列重载的 WaitOne方法,我们将使用后个如下所示:
public virtual bool WaitOne(int millisecondsTimeout);
当调用以上形式的WaitOne方法时,调用者线程将在由方法参数millisecondsTimeout指定的时间段内等待“等待句柄”对象的状态转为Signaled,此时
WaitOne方法返回true;如果超时,返回false.
示例代码如下:
[b]三、异步回调[/b]
前面两个示例使用轮询的方式不断询问异步调用是否完成,这无疑会在循环等待上浪费不少CPU时间。能不能让异步调用的方法在结束时自动调用一个方法,并在
这个方法中显示处理结果?
使用异步回调可以满足这个要求。
BeginInvoke 方法定义中的最后两个参数是 "AsyncCallback callback" 和 "object asyncState",这两个参数就是用于异步调用的。
以下是示例代码:
注意:
1、调用BeginInvoke方法的那句代码。BeginInvoke方法的第2个参数指定当异步调用结束时回调 ShowFolderSize方法,第3个参数asyncState被填入了要计算的文件夹
名字,此值被BeginInvoke方法包装到自动创建的一个 IAsyncResult类型的对象中,并作为方法实参自动传送给回调方法(即本示例中回调方法ShowFolderSize的参数result),
回调方法通过这一实参的AsyncState字段获取其值。
2、回调方法的返回值类型是void,只能有一个 IAsyncResult类型的参数result,并且要在方法体中调用 EndInvoke 方法以取回方法的执行结果,另外,result参数的AsyncState
属性包含了外界传入的参数信息(本例为文件夹名)。
这个程序现在可以连续输入多个文件夹名称,计算机在后台分别计算,完成后就在控制台窗口中输出结果。
这里还有一个注意点:先输入的文件夹(先执行的任务)并不一定先完成,可能后面执行的任务由于工作量小反而先执行完。
这里可以得出一个结论:如果需要将一些额外的信息传送给回调方法,就将其放入 BeginInvoke 方法的第3个参数asyncSate中。注意到
这个参数的类型为object ,所以可以放置任意类型的数据。 (在后面将会说到这个参数,其实类库中大多数异步的方法都有这个参数)!
可以有以下二种方法:
一、使用轮询
现在我们来改造上一节的示例程序。我们可以在程序执行异步调用的过程中,让计算机每隔一段时间向控制台输出一个小点,告诉用户搜索工作正在进行中,
从而可以大大改善程序的用户友好性。
示例程序:
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(); } } }
[b]三、异步回调[/b]
前面两个示例使用轮询的方式不断询问异步调用是否完成,这无疑会在循环等待上浪费不少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 ,所以可以放置任意类型的数据。 (在后面将会说到这个参数,其实类库中大多数异步的方法都有这个参数)!
相关文章推荐
- 异步编程模式(四):异步调用中的异常与任务同步
- 第十章(2)-等待异步调用的完成-学习笔记
- 异步编程模式(六):基于事件的异步调用模式
- 异步编程模式(五):实现 IAsyncResult 异步调用模式的组件
- 等待几个异步任务都完成后在执行其他的操作
- cmd 不等待命令执行完成 异步执行
- 使用NSRunloop等待异步任务完成
- C#中的异步调用及异步设计模式(三)——基于事件的异步模式
- 使用NSRunloop等待异步任务完成
- STM32 DMA正常模式等待传输完成和开始下一次传输
- CXF入门教程(5) -- webService异步调用模式
- C#中的异步调用及异步设计模式(一)
- xcode7 ios9 iOS中设计模式中委托模式(同一工作空间不同工程)异步调用
- 委托之异步调用 、抽象方法、策略模式、接口
- [你必须知道的异步编程]——基于任务的异步模式(TAP) 推荐
- C#中的异步调用及异步设计模式(二)——基于 IAsyncResult 的异步设计模式
- Linux下同步模式、异步模式、阻塞调用、非阻塞调用总结
- 为Windows应用创建简单的异步调用模式(附实例代码)
- 委托之异步调用 、抽象方法、策略模式、接口
- SCA编程模式三种方式的异步调用