【溫故知新】c#異步編程模型(APM)--使用委託進行異步編程

當咱們用到C#類許多耗時的函數XXX時,總會存在同名的相似BeginXXX,EndXXX這樣的函數。編程

例如Stream抽象類的Read函數就有異步

        public abstract int Read(byte[] buffer, int offset, int count);

        public virtual IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state);
        
        public virtual IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state);

這也就是所謂的APM異步編程模型,基於委託實現,其實就是啓動了一個後臺線程(主線程結束,則該線程結束),其中IAsyncResult就是異步狀態接口,用來檢測異步是否完成。async

代碼模擬場景1:

每到大熱天的中午就不想動,出門就是一身汗,因此通常午飯時間我都選擇定外賣,正好等外賣這段時間還能夠完善一下上午的工做內容。這就至關於委託一名外賣小哥異步幫我送一份快餐過來,這段時間我繼續幹個人事情。異步編程

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BeginInvoke
{
    class Program
    {
        //聲明一個送外賣的委託類型
        private delegate int DeliveryDelegate(string foodname);
static void Main(string[] args) { Console.WriteLine("天氣太熱,帶電話叫外賣小哥送一份番茄炒蛋"); //聲明一個外賣小哥,委託他去幫我拿午飯。 DeliveryDelegate DeliveryBoy = new DeliveryDelegate(GetFood); //開始CALL電話(異步調用,因爲還沒用到回調函數,後兩個參數填空) IAsyncResult ar = DeliveryBoy.BeginInvoke("番茄炒蛋", null, null); //繼續幹本身的事…… Console.WriteLine("完善上午的工做~大概5分鐘完成"); Thread.Sleep(1000); Console.WriteLine("完善20%"); Thread.Sleep(1000); Console.WriteLine("完善40%"); Thread.Sleep(1000); Console.WriteLine("完善60%"); Thread.Sleep(1000); Console.WriteLine("完善80%"); Thread.Sleep(1000); Console.WriteLine("完善100%"); Console.WriteLine("完善工做完成");//可是外賣還沒到 Console.WriteLine("等外賣……好餓,估計還要5分鐘纔到"); //此時調用EndInvoke會阻塞住線程直到異步調用完成,這時至關於我就主動蹲在門口等外賣小哥送上門,若是小哥先到,只能在門口等我辦完事~。 int money = DeliveryBoy.EndInvoke(ar); Console.WriteLine("給快遞小哥" + money + "元,開吃"); } /// <summary> /// 獲取食物 /// </summary> /// <param name="foodname">食物名</param> /// <returns>價格</returns> private static int GetFood(string foodname) { Console.WriteLine("外賣小哥頂着烈日送餐ing,預計10分鐘後到"); Thread.Sleep(10000); Console.WriteLine("外賣到達~" + foodname); //無論什麼菜一概10元錢! return 10; } } }

 

運行結果:函數

天氣太熱,帶電話叫外賣小哥送一份番茄炒蛋
完善上午的工做~大概5分鐘完成
外賣小哥頂着烈日送餐ing,預計10分鐘後到
完善20%
完善40%
完善60%
完善80%
完善100%
完善工做完成
等外賣……好餓,估計還要5分鐘纔到
外賣到達~番茄炒蛋
給快遞小哥10元,開吃spa

注:若是使用ar.AsyncWaitHandle.WaitOne();能夠設置指定等待時間間隔線程

            if (ar.AsyncWaitHandle.WaitOne())//也會阻塞住線程直到異步調用完成,EndInvoke以後會自動收到Set信號。
            {
                int money = DeliveryBoy.EndInvoke(ar);
                Console.WriteLine("給快遞小哥" + money + "元,開吃");
                //若是調用ar.AsyncWaitHandle.WaitOne();,記得EndInvoke後要主動Close喲!
                ar.AsyncWaitHandle.Close();
            }

代碼模擬場景2:

以前的場景,若是我沒有作完工做,那麼外賣小哥只能在門口一直等我作完事情(沒辦法通知我外賣到了,只有我主動去檢查),這個時候回調函數就派上用處了,我就不再用擔憂外賣何時到,由於我又委託一我的專門幫我在門口盯着外賣小哥!code

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BeginInvoke
{
    class Program
    {
        //聲明一個送外賣的委託類型
        private delegate int DeliveryDelegate(string foodname);
        static void Main(string[] args)
        {
            Console.WriteLine(DateTime.Now.ToString());
            Console.WriteLine("天氣太熱,帶電話叫外賣小哥送一份番茄炒蛋");

            //聲明一個外賣小哥,委託他去幫我拿午飯。
            DeliveryDelegate DeliveryBoy = new DeliveryDelegate(GetFood);

            //開始CALL電話(異步調用,AsyncCallback其實也是一個delegate類型,實例化delegate委託一我的專門幫我在門口盯着外賣小哥)
            IAsyncResult ar = DeliveryBoy.BeginInvoke("番茄炒蛋", new AsyncCallback(GetFoodCallback), "番茄炒蛋");

            //繼續幹本身的事……
            Console.WriteLine("完善上午的工做~大概5分鐘完成");
            Thread.Sleep(1000);
            Console.WriteLine("完善20%");
            Thread.Sleep(1000);
            Console.WriteLine("完善40%");
            Thread.Sleep(1000);
            Console.WriteLine("完善60%");
            Thread.Sleep(1000);
            Console.WriteLine("完善80%");
            Thread.Sleep(1000);
            Console.WriteLine("完善100%");
            Console.WriteLine("完善工做完成");//可是外賣還沒到

            Console.WriteLine("外賣估計還要5分鐘纔到,時間不等人,擼一盤");

            //中途外賣到了,回調函數幫我付錢拿外賣,我繼續擼。
            Console.WriteLine("LOL……一局10分鐘");
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine("過去了1分鐘");
            }

            Console.WriteLine("LOL拿到了首勝!,吃飯");

            if (ar.IsCompleted)
            {
                Console.WriteLine("好吃~");
            }
        }

        /// <summary>
        /// 獲取食物
        /// </summary>
        /// <param name="foodname">食物名</param>
        /// <returns>價格</returns>
        private static int GetFood(string foodname)
        {
            Console.WriteLine("外賣小哥頂着烈日送餐ing,預計10分鐘後到");
            Thread.Sleep(10000);
            Console.WriteLine("外賣到達~" + foodname);
            //無論什麼菜一概10元錢!
            return 10;
        }

        /// <summary>
        /// 異步完成,回調(完成時執行)
        /// 回調在 ThreadPool 線程上執行。 ThreadPool 線程是後臺線程,這些線程不會在主線程結束後保持應用程序的運行,所以示例的主線程必須休眠足夠長的時間以便回調完成。
        /// </summary>
        /// <param name="ar">異步狀態</param>
        private static void GetFoodCallback(IAsyncResult ar)
        {
            AsyncResult result = (AsyncResult)ar;
            DeliveryDelegate dd = result.AsyncDelegate as DeliveryDelegate;
            int money = dd.EndInvoke(ar);
            string foodname = ar.AsyncState as string;
            Console.WriteLine("拿外賣!" + foodname + ",一共" + money + "");
            Console.WriteLine("付錢");
        }
    }
}

運行結果:blog

天氣太熱,帶電話叫外賣小哥送一份番茄炒蛋
完善上午的工做~大概5分鐘完成
外賣小哥頂着烈日送餐ing,預計10分鐘後到
完善20%
完善40%
完善60%
完善80%
完善100%
完善工做完成
外賣估計還要5分鐘纔到,時間不等人,擼一盤
LOL……一局10分鐘
過去了1分鐘
過去了1分鐘
過去了1分鐘
過去了1分鐘
過去了1分鐘
外賣到達~番茄炒蛋
拿外賣!番茄炒蛋,一共10元
付錢
過去了1分鐘
過去了1分鐘
過去了1分鐘
過去了1分鐘
過去了1分鐘
LOL拿到了首勝!,吃飯
好吃~接口

 

參考:

https://msdn.microsoft.com/zh-cn/library/ms228963(v=vs.110).aspx

https://msdn.microsoft.com/zh-cn/library/system.iasyncresult(v=vs.110).aspx

https://msdn.microsoft.com/zh-cn/library/2e08f6yc(v=vs.110).aspx

相關文章
相關標籤/搜索