[C#学习笔记之异步编程模式2]BeginInvoke和EndInvoke方法 (转载)
为什么要进行异步回调?众所周知,普通方法运行,是单线程的,如果中途有大型操作(如:读取大文件,大批量操作数据库,网络传输等),都会导致方法阻塞,表现在界面上就是,程序卡或者死掉,界面元素不动了,不响应了。异步方法很好的解决了这些问题,异步执行某个方法,程序立即开辟一个新线程去运行你的方法,主线程包括界面就不会死掉了。异步调用并不是要减少线程的开销, 它的主要目的是让调用方法的主线程不需要同步等待在这个函数调用上, 从而可以让主线程继续执行它下面的代码.
BeginInvoke方法可以使用线程异步地执行委托所指向的方法。然后通过EndInvoke方法获得方法的返回值(EndInvoke方法的返回值就是被调用方法的返回值),或是确定方法已经被成功调用。当使用BeginInvoke异步调用方法时,如果方法未执行完,EndInvoke方法就会一直阻塞,直到被调用的方法执行完毕。
异步调用通用模板
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.IO;
- namespace AsyncCalculateFolderSize1
- {
- 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.WriteLine("正在计算中,请耐心等待……");
- //阻塞,等到调用完成,取出结果
- long size = d.EndInvoke(ret);
- Console.WriteLine("\n计算完成。文件夹{0}的容量为:{1}字节\n", FolderName, size);
- }
- }
- }
异步调用的奥秘
- public delegate long CalculateFolderSizeDelegate(string FolderName);
- public sealed class CalculateFolderSizeDelegate: MulticastDelegate
- {
- public CalculateFolderSizeDelegate(Object target , intmethodPtr)
- { …… }
- public virtual long invoke(string FolderName)
- { …… }
- public virtual IAsyncResult BeginInvoke( string FolderName,
- AsyncCallbackcallback , object asyncState)
- { …… }
- public virtual long EndInvoke( IAsyncResultresult )
- { …… }
- }
由此我们发现,当我们定义一个委托的时候,实际上是定义了一个委托类型,这个类型有invoke、BeginInvoke()、EndInvoke()这样几个成员方法,而这几个成员方法可以实现一步调用机制。我们看看这几个方法格式怎么定义的:
(1)BeginInvoke方法用于启动异步调用
BeginInvoke()的函数声明:
public IAsyncResult BeginInvoke(
<输入和输出变量>,回调函数callback , 附加信息AsyncState)
函数返回值类型:
public interface IAsyncResult
{
object AsyncState{ get;} //如果有回调函数的话该参数用于保存要传递给回调函数的参数值
WaitHandle AsyncWaitHandle{ get;}
bool CompletedSynchronously{ get;}
bool IsCompleted{ get;} //保存方法是否执行结束,我们可以通过该属性的值来判断异步方法是否执行结束
}
1.BeginInvoke返回IasyncResult,可用于监视调用进度。
2.结果对象IAsyncResult是从开始操作返回的,并且可用于获取有关异步开始操作是否已完成的状态。
3.结果对象被传递到结束操作,该操作返回调用的最终返回值。
4.在开始操作中可以提供可选的回调。如果提供回调,在调用结束后,将调用该回调;并且回调中的代码可以调用结束操作。
5.如果需要将一些额外的信息传送给回调函数,就将其放入BeginInvoke()方法的第3个参数asyncState中。注意到这个参数的类型为Object,所以可以放置任意类型的数据。如果有多个信息需要传送给回调函数,可以将所有要传送的信息封状到一个Struct变量,或者干脆再定义一个类,将信息封装到这个类所创建的对象中,再传送给BeginInvoke()方法。
(2)EndInvoke方法用于检索异步调用结果。
方法声明:
public <方法返回值类型>EndInvoke(<声明为ref或out的参数>, IAsyncResult result )
1.result参数由BeginInvoke()方法传回。.NET借此以了解方法调用是否完成。
2.当EndInvoke方法发现异步调用完成时,它取出此异步调用方法的返回值作为其返回值,如果异步调用方法有声明为ref和out的参数,它也负责填充它。
3.在调用BeginInvoke后可随时调用EndInvoke方法,注意:始终在异步调用完成后调用EndInvoke.
4.如果异步调用未完成,EndInvoke将一直阻塞到异步调用完成。
5.EndInvoke的参数包括需要异步执行的方法的out和ref参数以及由BeginInvoke返回的IAsyncResult。
应用实例:
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.IO;
- 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 ("正在计算中,请耐心等待");
- //每隔2秒检查一次,输出一个“."
- while (ret.IsCompleted == false)
- {
- Console.Write(".");
- System.Threading.Thread.Sleep(200);
- }
- //阻塞,等到调用完成,取出结果
- long size = d.EndInvoke(ret);
- Console.WriteLine("\n计算完成!\n文件夹{0}的容量为:{1}字节", FolderName, size);
- }
- }
- }
这样,当程序在执行CalculateFolderSize这个异步方法的时候主线程并不是“假死”,而是每隔0.2毫秒输出一个“.",这就是异步调用的妙处!
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.IO;
- 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);
- }
- }
- }
3.使用异步回调函数
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.IO;
- 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 task = CalculateFolderSize;
- //用于回调的函数
- public static void ShowFolderSize(IAsyncResult result)
- {
- long size = task.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;
- task.BeginInvoke(FolderName, ShowFolderSize, FolderName);//第一个参数是异步函数的参数,第二个参数是回调函数,第三个参数是回调函数的参数,回调函数会在异步函数执行结束之后被调用。
- }
- }
- }
- }
这个例子中通过循环的输入文件夹名称计算文件夹容量,计算的操作放在异步调用函数中,因此我们在输入下一个文件夹名称时不必等待上一个计算结束,异步函数执行完成之后会自动调用回调函数ShowFolderSize进行结果处理。



- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.IO;
- namespace AsyncCalculateFolderSize6
- {
- 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 = new CalculateFolderSizeDelegate(CalculateFolderSize);
- //用于回调的函数
- public static void ShowFolderSize(IAsyncResult result)
- {
- try
- {
- long size = d.EndInvoke(result);
- while (Console.CursorLeft != 0)//只有用户不输入,且光标位于第一列时,才输出信息。
- {
- //等待2秒
- System.Threading.Thread.Sleep(2000);
- }
- Console.WriteLine("\n文件夹{0}的容量为:{1}字节\n", (String)result.AsyncState, size);
- }
- catch (DirectoryNotFoundException e)
- {
- Console.WriteLine("您输入的文件夹不存在");
- }
- }
- 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);
- }
- }
- }
- }
Invoke() 调用时,会阻塞当前线程,等到 Invoke() 方法返回才继续执行后面的代码,表现出“同步”的概念。
BeginInvoke() 调用时,当前线程会启用线程池中的某个线程来执行此方法,当前线程不被阻塞,继续运行后面的代码,表现出“异步”的概念。
EndInvoke() ,在想获取 BeginInvoke() 执行完毕后的结果时,调用此方法来获取。
[C#学习笔记之异步编程模式2]BeginInvoke和EndInvoke方法 (转载)的更多相关文章
- 异步使用委托delegate --- BeginInvoke和EndInvoke方法
当我们定义一个委托的时候,一般语言运行时会自动帮委托定义BeginInvoke 和 EndInvoke两个方法,这两个方法的作用是可以异步调用委托. 方法BeginInvoke有两个参数: Async ...
- Python学习笔记之面向对象编程(三)Python类的魔术方法
python类中有一些方法前后都有两个下划线,这类函数统称为魔术方法.这些方法有特殊的用途,有的不需要我们自己定义,有的则通过一些简单的定义可以实现比较神奇的功能 我主要把它们分为三个部分,下文也是分 ...
- 多线程编程学习笔记——使用异步IO(一)
接上文 多线程编程学习笔记——使用并发集合(一) 接上文 多线程编程学习笔记——使用并发集合(二) 接上文 多线程编程学习笔记——使用并发集合(三) 假设以下场景,如果在客户端运行程序,最的事情之一是 ...
- 多线程编程学习笔记——使用异步IO
接上文 多线程编程学习笔记——使用并发集合(一) 接上文 多线程编程学习笔记——使用并发集合(二) 接上文 多线程编程学习笔记——使用并发集合(三) 假设以下场景,如果在客户端运行程序,最的事情之一是 ...
- 【专栏学习】APM——异步编程模型(.NET不推荐)
(1)learning hard C#学习笔记 异步1:<learning hard C#学习笔记>读书笔记(20)异步编程 (2)<C# 4.0 图解教程> 22.4 异步编 ...
- ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则
ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...
- 孙鑫VC学习笔记:多线程编程
孙鑫VC学习笔记:多线程编程 SkySeraph Dec 11st 2010 HQU Email:zgzhaobo@gmail.com QQ:452728574 Latest Modified ...
- Hadoop学习笔记(7) ——高级编程
Hadoop学习笔记(7) ——高级编程 从前面的学习中,我们了解到了MapReduce整个过程需要经过以下几个步骤: 1.输入(input):将输入数据分成一个个split,并将split进一步拆成 ...
- WCF学习笔记之事务编程
WCF学习笔记之事务编程 一:WCF事务设置 事务提供一种机制将一个活动涉及的所有操作纳入到一个不可分割的执行单元: WCF通过System.ServiceModel.TransactionFlowA ...
随机推荐
- angular 常用写法
1.ng-repeat 数组数据中,不允许数组中有相同的两个数据,这个时候用下标去管理数据便可以解决这个问题 ng-repeat="item in list track by $index& ...
- setTimeout()传带有参数的函数
w3cshool里的解释:setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式,语法:setTimeout(code,millisec). 也就是说,第一个参数可以是字符串形式的Ja ...
- java 监听控制台输入
分享一下我写的java监听控制台输入并可以给出响应的功能. 很多时候需要监听控制台的输入内容,相当于信号监听,根据输入的内容做出相应的动作,这里给出我的一个简单实现. 要注意的是:监听得到的消息中前后 ...
- WTM
WTM的由来 WalkingTec.Mvvm框架(简称WTM)最早开发与2013年,基于Asp.net MVC3 和 最早的Entity Framework, 当初主要是为了解决公司内部开发效率低,代 ...
- [CodeForces]786B Legacy
线段树优化建图. 建立两棵线段树,其上点的点权分别表示"到达这个区间内所有点的最小花费"和"到达这个区间内任意一个点的最小花费". 对于第一种路直接加边即可 对 ...
- java实现根据起点终点和日期查询去哪儿网的火车车次和火车站点信息
本文章为原创文章,转载请注明,欢迎评论和改正. 一,分析 之前所用的直接通过HTML中的元素值来爬取一些网页上的数据,但是一些比较敏感的数据,很多正规网站都是通过json数据存储,这些数据通过HTML ...
- 小松之LINUX 驱动学习笔记(一)
本篇主要是讲解驱动开发的基础知识以及一些环境配置方面的问题. 下面是一个hello world的简单的模块代码,很简单./*********************** 模块的简单例子* author ...
- tableView计算动态行高的总结
研究tableView怎么计算动态行高研究了两天一直还不太会,今天最终做出来了想要的效果. 首先.我在网上搜集了非常多资料,各种大神的总结,然后開始看.研究.试验,基本思路都是一样的. 1.一定要将l ...
- “XXX.Index”不扩展类“System.Web.UI.Page”,因此此处不同意的问题
"XXX.Index"不扩展类"System.Web.UI.Page",因此此处不同意的问题 原因:设计页面继承的路径和后台.cs页面类的路径不一致造成的 看下 ...
- HDU 3861--The King’s Problem【scc缩点构图 && 二分匹配求最小路径覆盖】
The King's Problem Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Other ...