1. 程式人生 > >非同步程式設計-模擬非同步呼叫

非同步程式設計-模擬非同步呼叫

傳統應用程式在呼叫一個方法時,需要等待該方法執行完成並返回呼叫處,然後再繼續執行呼叫處後面的語句,但如果呼叫的方法本身需要執行較長的時間,則程式將長時間的等待。如果希望在呼叫一個方法時,能在該方法沒有執行完成的時候繼續執行其他程式碼,則需要非同步程式設計。

非同步程式設計的基本思想是,向其他元件發出方法呼叫,繼續執行其他任務,而不用等待呼叫的操作完成

在多執行緒程式設計中,每個執行緒同時執行各自的任務,開發者必須在應用程式中建立並管理這些執行緒。非同步程式設計也可以達到多執行緒效果,不同的是非同步程式設計不需要建立多個執行緒,只需在主執行緒中發出一個非同步呼叫,而不需要等待非同步呼叫返回即可繼續執行其他操作。如果需要返回非同步呼叫結果,則需通過回撥,輪詢等方式來獲得。

所有的非同步呼叫都是由主執行緒發起,且獨立於主執行緒之外單獨執行,這樣不但達到了多執行緒的效果,而且還避免了多執行緒的同步問題。因此,使用非同步程式設計來執行多個任務要更簡便些。

非同步程式設計一般有兩個邏輯部分:客戶端呼叫開始方法並提供引數,從而啟動非同步操作;客戶端通過呼叫結束方法,來獲取非同步操作的結果。

1、開始非同步操作

呼叫方在呼叫開始方法時,除了提供必要的引數外,還可以提供一個可選的AsyncCallback委託,用來設定回撥函式。開始方法會同步返回一個實現IAsyncResult介面的物件,呼叫方可以使用該介面的屬性和方法來確定非同步操作的狀態或結果。

2、獲取非同步操作的結果

當操作完成時,呼叫者可以通過許多方法來獲取操作結果。

首先是回撥函式,如果提供了可選的AsyncCallback委託,那麼當操作完成時,自動引用並執行該回調函式。

對於沒有顯示實現非同步呼叫的方法,開發人員可以使用委託技術來獲取操作結果。委託不但提供了對方法的封裝,還提供了對方法進行非同步呼叫的介面。編譯器為每個委託類生成BeginInvoke和EndInvoke方法,用來實現非同步呼叫。如果呼叫BeginInvoke()方法,則執行環境將對請求進行排隊,並立即返回到呼叫方,來自執行緒池的某個執行緒將呼叫該方法,提交請求的原始執行緒將繼續執行。

如果在BeginInvoke()方法中指定了回撥方法,則當非同步方法返回時,將呼叫該方法,而在回撥中,使用EndInvoke()方法來獲取返回值和輸出引數。

執行結果:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.Remoting.Contexts;

namespace Test1_1
{
    class HelloWorld
    {
        public delegate string AsyncDelegate(int callTime);               //定義一個委託,用來提供非同步呼叫的介面
        static void Main(string[] args)
        {
            AsyncDemo ad = new AsyncDemo();
            AsyncDelegate dlgy = new AsyncDelegate(ad.text);              //封裝要非同步呼叫的方法
            //開始非同步呼叫(指定該方法的執行時間3秒),並指定回撥方法
            IAsyncResult ar = dlgy.BeginInvoke(3000, new AsyncCallback(CallbackMethod), dlgy);
            //程式繼續執行
            Console.WriteLine("主執行緒繼續工作");               
            //模擬主程式需要執行的時間(1秒)
            Thread.Sleep(1000);
            Console.WriteLine("主執行緒工作完成,執行了1秒,等待非同步呼叫完成");

            Console.Read();
        }
        //非同步呼叫結束後的回撥方法
        static void CallbackMethod(IAsyncResult ar)
        {
            //從非同步操作的狀態中提取AsyncDelegate委託
            AsyncDelegate dlgy = (AsyncDelegate)ar.AsyncState; 
            //獲取非同步呼叫的結果
            string result = dlgy.EndInvoke(ar);
            Console.WriteLine("非同步呼叫完成,{0}", result);
        }
    }

    public class AsyncDemo
    {
        //非同步呼叫的測試方法
        public string text(int callTime)
        {
            Console.WriteLine("非同步呼叫的方法開始");
            //模擬該方法需要執行的時間(3秒)
            Thread.Sleep(callTime);

            return "方法需要時間" + (callTime / 1000) + "秒";
        }
    }
}

回撥方法為CallbackMethod(),該方法返回值為void,並有一個IAsyncResult型別的引數。System名稱空間定義了一個AsyncCallback委託類,該類與此方法簽名匹配,因此無需宣告新的委託型別。

該主執行緒呼叫test()方法後,並沒有待方法返回,而是繼續向下執行,同時,非同步呼叫的方法也開始了執行。由於主執行緒只需執行1秒而子執行緒需執行3秒,所以主執行緒完成,兩秒後非同步呼叫結束。

如果沒有BeginInvoke()方法中指定回撥,則可以在提交請求的程式中使用其他非同步設計模式技術。

輪詢:呼叫方可以輪詢檢查IAsyncResult介面的IsCompleted屬性,來確定呼叫是否完成。

執行結果:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.Remoting.Contexts;

namespace Test1_1
{
    class HelloWorld
    {
        public delegate string AsyncDelegate(int callTime);               //定義一個委託,用來提供非同步呼叫的介面
        static void Main(string[] args)
        {
            AsyncDemo ad = new AsyncDemo();
            AsyncDelegate dlgy = new AsyncDelegate(ad.text);              //封裝要非同步呼叫的方法
            //開始非同步呼叫(指定該方法的執行時間3秒),並指定回撥方法
            IAsyncResult ar = dlgy.BeginInvoke(3000, null, null);

            //程式繼續執行
            Console.WriteLine("主執行緒繼續工作");               
            //模擬主程式需要執行的時間(1秒)
            Thread.Sleep(1000);
            Console.WriteLine("主執行緒工作完成,執行了1秒,等待非同步呼叫完成");
            //輪詢非同步呼叫是否完成
            while (!ar.IsCompleted)
            {
                //模擬主程式下次輪詢的時間(1秒)
                Thread.Sleep(1000);
                Console.WriteLine("主執行緒工作完成,等待非同步呼叫完成");
            }
            //呼叫結束方法,獲取呼叫結果
            string result = dlgy.EndInvoke(ar);
            Console.WriteLine("非同步呼叫完成,{0}", result);

            Console.Read();
        }
    }

    public class AsyncDemo
    {
        //非同步呼叫的測試方法
        public string text(int callTime)
        {
            Console.WriteLine("非同步呼叫的方法開始");
            //模擬該方法需要執行的時間(3秒)
            Thread.Sleep(callTime);

            return "方法需要時間" + (callTime / 1000) + "秒";
        }
    }
}

主執行緒呼叫test()方法後,繼續向下執行,同時,非同步呼叫的方法也開始執行,由於主執行緒只需執行1秒,執行結束後,開始每隔1秒輪詢非同步呼叫是否完成,而非同步呼叫需執行3秒,所以可以在兩次輪詢後非同步呼叫結束。