最近看书,看到了可以利用学过的委托知识实现异步编程,这里做一个简单的说明示例。如果委托对象在调用列表中只有一个方法(引用方法),他就可有异步执行这个方法。委托类有两个方法,BeginInvoke和EndInvoke。
- 当我们调用委托的BeginInvoke方法时,它开始在一个独立线程上执行引用方法。并且立即返回到原始线程。原始线程可以继续。而引用方法会在线程池中的线程中并行执行。
- 当程序希望获取已完成的异步方法的结果时,可以检查BeginInvoke返回的IAsyncResult的IsCompleted属性。或调用委托的EndInvoke方法来等待委托完成。
等待-直到完成 模式
delegate string MyDownLoadDel(string weburl); static void Main(string[] args) { Stopwatch watch = new Stopwatch(); watch.Start(); Debug.WriteLine(string.Format("开始计时:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds)); MyDownLoadDel del = new MyDownLoadDel(DownLoadWeb); IAsyncResult iar = del.BeginInvoke("https://www.asp.net/", null, null); Debug.WriteLine(string.Format("委托BeginInvoke:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds)); string str = del.EndInvoke(iar); Debug.WriteLine(string.Format("EndInvoke,下载网站大小:{0}耗时:{1,4:N0}ms", str.Length, watch.Elapsed.TotalMilliseconds)); Console.ReadKey(); //暂停 } public static string DownLoadWeb(string url) { WebClient wc = new WebClient(); string str = wc.DownloadString(url); Debug.WriteLine(string.Format("下载网站{0}结束", url)); return str; }
返回的结果为:
开始计时: 0ms 委托BeginInvoke: 33ms 下载网站https://www.asp.net/结束 EndInvoke,下载网站大小:30543耗时:1,025ms
轮询模式
delegate string MyDownLoadDel(string weburl); static void Main(string[] args) { Stopwatch watch = new Stopwatch(); watch.Start(); Debug.WriteLine(string.Format("开始计时:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds)); MyDownLoadDel del = new MyDownLoadDel(DownLoadWeb); IAsyncResult iar = del.BeginInvoke("https://www.asp.net/", null, null); Debug.WriteLine(string.Format("委托BeginInvoke:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds)); while (!iar.IsCompleted) { Debug.WriteLine(string.Format("还没有完成:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds)); Thread.Sleep(1000); } string str = del.EndInvoke(iar); Debug.WriteLine(string.Format("EndInvoke,下载网站大小:{0}耗时:{1,4:N0}ms", str.Length, watch.Elapsed.TotalMilliseconds)); Console.ReadKey(); //暂停 } public static string DownLoadWeb(string url) { WebClient wc = new WebClient(); string str = wc.DownloadString(url); Debug.WriteLine(string.Format("下载网站{0}结束", url)); return str; }
返回结果为:
开始计时: 0ms 委托BeginInvoke: 29ms 还没有完成: 29ms 还没有完成:1,052ms 还没有完成:2,059ms 还没有完成:3,066ms 还没有完成:4,078ms 下载网站https://www.asp.net/结束 EndInvoke,下载网站大小:30561耗时:5,084ms
回调模式:
回调模式和前面的不同之处在于,一旦初始线程发起了异步方法,它就自己管自己,不在考虑同步。当异步方法调用结束之后,系统调用一个用户自定义的方法来处理结果。并且返回委托的EndInvoke方法。这个用户自定义方法称为回调或者回调函数。
BeginInvoke的参数列表中最后两个额外的参数由回调函数使用。
- 第一个参数callback,是回调方法的名字。
- 第二个参数state,可以是null或传入回调方法的一个对象的引用。我们可以通过使用IAsyncResult参数的AsyncState属性来获取这个对象,参数的类型是object。
delegate string MyDownLoadDel(string weburl); static void Main(string[] args) { Stopwatch watch = new Stopwatch(); watch.Start(); Debug.WriteLine(string.Format("开始计时:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds)); MyDownLoadDel del = new MyDownLoadDel(DownLoadWeb); IAsyncResult iar = del.BeginInvoke("https://www.asp.net/", new AsyncCallback(CallWhenDone), null); //new AsyncCallback 表示异步操作完成调用,如果直接写CallWhenDone,那么程序运行是同步完成 Debug.WriteLine(string.Format("委托BeginInvoke:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds)); Thread.Sleep(1000); Debug.WriteLine(string.Format("主线程运行耗时:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds)); Console.ReadKey(); //暂停 } public static string DownLoadWeb(string url) { WebClient wc = new WebClient(); string str = wc.DownloadString(url); Debug.WriteLine(string.Format("下载网站{0}结束", url)); return str; } public static void CallWhenDone(IAsyncResult iar) { Debug.WriteLine(string.Format("进入回调方法体")); AsyncResult ar = (AsyncResult)iar; //获取类对象的引用 MyDownLoadDel del = (MyDownLoadDel)ar.AsyncDelegate; //获取委托的引用 string str = del.EndInvoke(iar); //调用EndInvoke Debug.WriteLine(string.Format("回调方法获取结果为Length:{0}", str.Length)); }
结果如下:
开始计时: 0ms 委托BeginInvoke: 26ms 主线程运行耗时:1,029ms 下载网站https://www.asp.net/结束 进入回调方法体 回调方法获取结果为Length:30543
修改BeginInvoke的第二个state参数,我们传入del,可以直接用iar.AsyncState在回调函数中获取委托的引用。代码如下:
delegate string MyDownLoadDel(string weburl); static void Main(string[] args) { Stopwatch watch = new Stopwatch(); watch.Start(); Debug.WriteLine(string.Format("开始计时:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds)); MyDownLoadDel del = new MyDownLoadDel(DownLoadWeb); IAsyncResult iar = del.BeginInvoke("https://www.asp.net/", new AsyncCallback(CallWhenDone), del); //new AsyncCallback 表示异步操作完成调用,如果直接写CallWhenDone,那么程序运行是同步完成 Debug.WriteLine(string.Format("委托BeginInvoke:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds)); Thread.Sleep(1000); Debug.WriteLine(string.Format("主线程运行耗时:{0,4:N0}ms", watch.Elapsed.TotalMilliseconds)); Console.ReadKey(); //暂停 } public static string DownLoadWeb(string url) { WebClient wc = new WebClient(); string str = wc.DownloadString(url); Debug.WriteLine(string.Format("下载网站{0}结束", url)); return str; } public static void CallWhenDone(IAsyncResult iar) { Debug.WriteLine(string.Format("进入回调方法体")); MyDownLoadDel del = (MyDownLoadDel)iar.AsyncState; //获取委托的引用 string str = del.EndInvoke(iar); //调用EndInvoke Debug.WriteLine(string.Format("回调方法获取结果为Length:{0}", str.Length)); }
结果如下:
开始计时: 0ms 委托BeginInvoke: 26ms 主线程运行耗时:1,027ms 下载网站https://www.asp.net/结束 进入回调方法体 回调方法获取结果为Length:30561
可以看到主线程的运行没有影响,是在执行完DownLoadWeb方法后才会仅仅回调方法CallWhenDone中。
参考文档:https://msdn.microsoft.com/zh-cn/library/system.iasyncresult(v=vs.110).aspx
来源:https://www.cnblogs.com/hueychan/p/10575906.html