同步回調 與 異步回調

做者:二律背反
連接:https://www.zhihu.com/question/47371217/answer/105797161
來源:知乎
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

回調,其實就是把一個函數做爲參數傳遞給另外一個函數;回調能夠是同步的也能夠是異步的;同步異步和單線程多線程沒有關係。或者說,同步也能夠是單線程也能夠是多線程;異步則必須是多線程或者多進程(每一個進程能夠是單線程)。javascript




========== 請確保到此爲止沒有任何問題,若是有問題,就先不要往下看 ===========java


事件機制,必然用到回調函數,有的回調是同步的,有的回調是異步的。多線程

若是我沒記錯的話,C語言的printf回調了相應的函數,這個過程是同步的(好比你在某個地方printf裏大量數據,那麼程序就會卡在這裏,一直到全部的數據都print了才繼續日後走);javascript的print是異步的(不肯定,記不清了)。異步

同步回調就是:把函數b傳遞給函數a。執行a的時候,回調了b,a要一直等到b執行完才能繼續執行;函數

異步回調就是:把函數b傳遞給函數a。執行a的時候,回調了b,而後a就繼續日後執行,b獨自執行。---請注意這裏不要和多線程糾纏起來:線程是存在於一個進程裏的,而異步回調多是好幾個進程。oop

另外,鼠標點擊的event不必定是異步的但必定是非阻塞的spa

好比全部的event都對應一個消息循環機制(main event loop),當我有一個event(好比鼠標點擊)時,就觸發一個消息,而後該消息就回調一個函數,而這個被回調的函數要執行完纔會繼續往下走---這時就是同步的。線程

爲何必定是非阻塞的呢?很好理解,若是是阻塞的,那麼假如某個event的回調函數須要執行1分鐘,那麼在這1分鐘以內,起他event就都不能被處理了。code


固然,對於非阻塞的main event loop,咱們能夠用單進程多線程來實現,但至因而不是必須用異步回調,這個不必定。blog

while(true)    // main event loop ----- 必定是非阻塞的
{
    爲鼠標event建立一個線程---回調函數a---函數a不執行完這個線程就一直等着(同步回調)
    爲輸出流建立一個線程---回調函數b---函數b執行同時線程繼續日後執行(異步回調)
}


同步調用

委託的Invoke方法用來進行同步調用。同步調用也能夠叫阻塞調用,它將阻塞當前線程,而後執行調用,調用完畢後再繼續向下進行。

namespace SyncInvoke
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("---------同步調用AsyncTest------------");
            AddHandler handler = new AddHandler(PlusClass.Add);
           int result= handler.Invoke(2, 3);
           Console.WriteLine("繼續作別的事");
           Console.WriteLine(result);
           Console.ReadKey();
        }
    }
    public delegate int AddHandler(int a,int b);
    class PlusClass
    {
        public static int Add(int a, int b)
        {
            Console.WriteLine("開始計算" + a + "+" + b);
            Thread.Sleep(3000);
            Console.WriteLine("計算完成");
            return a + b;
        }
    }
}

同步調用會阻塞線程,若是是要調用一項繁重的工做(如大量IO操做),可能會讓程序停頓很長時間,形成糟糕的用戶體驗,這時候異步調用就頗有必要了。

異步調用

異步調用不阻塞線程,而是把調用塞到線程池中,程序主線程或UI線程能夠繼續執行。
委託的異步調用經過BeginInvoke和EndInvoke來實現。

namespace AsyncTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("-----異步調用AsyncTest------");
            AddHandler handler = new AddHandler(PlusClass.Add);
            var result = handler.BeginInvoke(2, 3, null, null);
            Console.WriteLine("繼續作別的事");
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("...");
                Thread.Sleep(500);
            }
            Console.WriteLine(handler.EndInvoke(result));

            Console.ReadKey();
        }
    }
    public delegate int AddHandler(int a,int b);
    class  PlusClass
    {
        public static int Add(int a, int b)
        {
            Console.WriteLine("開始計算" + a + "+" + b);
            Thread.Sleep(3000);
            Console.WriteLine("計算完成");
            return a + b;
        }
    }
}

能夠看到,主線程並無等待,而是直接向下運行了。
可是問題依然存在,當主線程運行到EndInvoke時,若是這時調用沒有結束(這種狀況極可能出現),這時爲了等待調用結果,線程依舊會被阻塞。
 

 異步委託,也能夠參考以下寫法:

Action<object> action=(obj)=>method(obj);
action.BeginInvoke(obj,ar=>action.EndInvoke(ar),null);

簡簡單單兩句話就能夠完成一部操做。

相關文章
相關標籤/搜索