.net利用委托BeginInvoke和EndInvoke实现异步编程

旧街凉风 提交于 2020-01-27 19:02:56

最近看书,看到了可以利用学过的委托知识实现异步编程,这里做一个简单的说明示例。如果委托对象在调用列表中只有一个方法(引用方法),他就可有异步执行这个方法。委托类有两个方法,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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!