同步回調 與 異步回調


作者:二律背反
鏈接:https://www.zhihu.com/question/47371217/answer/105797161
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

回調,其實就是把一個函數作為參數傳遞給另一個函數;回調可以是同步的也可以是異步的;同步異步和單線程多線程沒有關系。或者說,同步也可以是單線程也可以是多線程;異步則必須是多線程或者多進程(每個進程可以是單線程)。




========== 請確保到此為止沒有任何問題,如果有問題,就先不要往下看 ===========


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

如果我沒記錯的話,C語言的printf回調了相應的函數,這個過程是同步的(比如你在某個地方printf里大量數據,那么程序就會卡在這里,一直到所有的數據都print了才繼續往后走);javascript的print是異步的(不確定,記不清了)。

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

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

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

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

為什么一定是非阻塞的呢?很好理解,如果是阻塞的,那么假如某個event的回調函數需要執行1分鍾,那么在這1分鍾之內,起他event就都不能被處理了。


當然,對於非阻塞的main event loop,我們可以用單進程多線程來實現,但至於是不是必須用異步回調,這個不一定。

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);

簡簡單單兩句話就可以完成一部操作。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM