- 浏览: 526170 次
文章分类
最新评论
-
chaodilei:
我可以提个意见吗?作为前端工程师,写博客应该注意段落标题的ba ...
我的前端之路 -
zhuchao_ko:
大家一起二。
我的前端之路
[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毫秒输出一个“.",这就是异步调用的妙处!
2. 使用轮询等待异步调用完成:使用IAsyncResult的AsyncWaitHandle.WaitOne
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); } } } }
相关推荐
本文开始C#线程系列讲座之一,即BeginInvoke和EndInvoke的使用方法,需要的朋友可以参考下
Windows 应用程序编程中常见的一个模式就是,在GUI用户界面下,将耗时的文件和网络处理放入 子线程,以避免用户界面不能响应的问题。在.NET出现以前,创建线 程并监视线程结束,还要更新 界面等工作,即复杂又要手写...
使用委托,事件和BeginInvoke从另外一个cs文件向form.cs文件传递数据,并把数据赋值给窗体控件
今天小编就为大家分享一篇关于C#用委托BeginInvoke做异步线程,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
异步编程(AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用小总结)
C#窗体中Invoke和BeginInvoke方法详解
直接调用EndInvoke 方法等待异步调用结束 异步执行方法的最简单的方式是通过调用委托的BeginInvoke方法来开始执行方法,在主线程上执行一些...下面的代码说明了如何使用这种方法来进行异步调用,并获得异步方法的结果:
公共语言运行库将自动为该委托定义具有适当签名的 BeginInvoke 和 EndInvoke 方法。 BeginInvoke 方法用于启动C#异步调用。它与您需要异步执行的方法具有相同的参数,只不过还有两个额外的参数(将在稍后描述)。...
本篇文章是对使用BeginInvoke,EndInvoke异步调用委托的实现代码进行了分析介绍,需要的朋友参考下
C#线程系列讲座笔记整理, (1)BeginInvoke和EndInvoke方法 (2)Thread类的应用 (3)线程池和文件下载服务器 (4)同步与死锁
同步方法和异步方法的区别同步方法调用在程序继续执行之前需要等待同步方法执行完毕返回结果异步方法则在被调用之后立即返回以便程序在被调用方法完成其任务的同时执行其它操作异步编程概览.NET Framework 允许您...
最近在我参与的几个.Net项目中都有用到异步编程,作为一名.Net小白,很...在C#语言中,主要是通过委托来实现异步编程的,在委托类型中定义了两个方法BeginInvoke()和EndInvoke()。 /// /// 开始执行异步操作. /// /
.NET Compact Framework 中不支持异步委托调用,也就是 BeginInvoke 和 EndInvoke 方法。 BeginInvoke 方法启动异步调用。该方法与您需要异步执行的方法具有相同的参数,还有另外两个可选参数。第一个参数是一个 ...
委托类有两个方法,叫做BeginInvoke和EndInvoke,它们是用来异步执行使用。 异步有三种模式 等待模式,在发起了异步方法以及做了一些其它处理之后,原始线程就中断,并且等待异步方法完成之后再继续。 轮询模式,...
C#异步调用: .NET Framework 允许您C#异步调用任何方法。定义与您需要调用的方法具有相同签名的委托;公共语言运行库将自动为该委托定义具有适当签名的 BeginInvoke 和 EndInvoke 方法。
本程序详细介绍了线程和线程池的用法,使用多线程进行和异步编程实现数据库操作和日志的记录
如果启动异步调用的线程是不需要处理结果的线程,则可以在调用完成时执行回调方法。回调方法在线程池线程上执行。 若要使用回调方法,必须...下面的代码说明了如何使用这种方法来进行异步调用,并获得异步方法的结果: