什麼是協程 說到協程,我們先瞭解什麼是非同步,非同步簡單說來就是,我要發起一個調用,但是這個被調用方(可能是其它線程,也可能是IO)出結果需要一段時間,我不想讓這個調用阻塞住調用方的整個線程,因此傳給被調用方一個回調函數,被調用方運行完成後回調這個回調函數就能通知調用方繼續往下執行。舉個例子:下麵的代碼 ...
什麼是協程
說到協程,我們先瞭解什麼是非同步,非同步簡單說來就是,我要發起一個調用,但是這個被調用方(可能是其它線程,也可能是IO)出結果需要一段時間,我不想讓這個調用阻塞住調用方的整個線程,因此傳給被調用方一個回調函數,被調用方運行完成後回調這個回調函數就能通知調用方繼續往下執行。舉個例子:
下麵的代碼,主線程一直迴圈,每迴圈一次sleep 1毫秒,計數加一,每10000次列印一次。
private static void Main() { int loopCount = 0; while (true) { int temp = watcherValue; Thread.Sleep(1); ++loopCount; if (loopCount % 10000 == 0) { Console.WriteLine($"loop count: {loopCount}"); } } }
這時我需要加個功能,在程式一開始,我希望在5秒鐘之後列印出loopCount的值。看到5秒後我們可以想到Sleep方法,它會阻塞線程一定時間然後繼續執行。我們顯然不能在主線程中Sleep,因為會破壞掉每10000次計數列印一次的邏輯。
// example2_1 class Program { private static int loopCount = 0; private static void Main() { OneThreadSynchronizationContext _ = OneThreadSynchronizationContext.Instance; WaitTimeAsync(5000, WaitTimeFinishCallback); while (true) { OneThreadSynchronizationContext.Instance.Update(); Thread.Sleep(1); ++loopCount; if (loopCount % 10000 == 0) { Console.WriteLine($"loop count: {loopCount}"); } } } private static void WaitTimeAsync(int waitTime, Action action) { Thread thread = new Thread(()=>WaitTime(waitTime, action)); thread.Start(); } private static void WaitTimeFinishCallback() { Console.WriteLine($"WaitTimeAsync finsih loopCount的值是: {loopCount}"); } /// <summary> /// 在另外的線程等待 /// </summary> private static void WaitTime(int waitTime, Action action) { Thread.Sleep(waitTime); // 將action扔回主線程執行 OneThreadSynchronizationContext.Instance.Post((o)=>action(), null); } }
我們在這裡設計了一個WaitTimeAsync方法,WaitTimeAsync其實就是一個典型的非同步方法,它從主線程發起調用,傳入了一個WaitTimeFinishCallback回調方法做參數,開啟了一個線程,線程Sleep一定時間後,將傳過來的回調扔回到主線程執行。OneThreadSynchronizationContext是一個跨線程隊列,任何線程可以往裡面扔委托,OneThreadSynchronizationContext的Update方法在主線程中調用,會將這些委托取出來放到主線程執行。為什麼回調方法需要扔回到主線程執行呢?因為回調方法中讀取了loopCount,loopCount在主線程中也有讀寫,所以要麼加鎖,要麼永遠保證只在主線程中讀寫。加鎖是個不好的做法,代碼中到處是鎖會導致閱讀跟維護困難,很容易產生多線程bug。這種將邏輯打包成委托然後扔回另外一個線程是多線程開發中常用的技巧。
我們可能又有個新需求,WaitTimeFinishCallback執行完成之後,再想等3秒,再列印一下loopCount。
private static void WaitTimeAsync(int waitTime, Action action) { Thread thread = new Thread(()=>WaitTime(waitTime, action)); thread.Start(); } private static void WaitTimeFinishCallback() { Console.WriteLine($"WaitTimeAsync finsih loopCount的值是: {loopCount}"); WaitTimeAsync(3000, WaitTimeFinishCallback2); } private static void WaitTimeFinishCallback2() { Console.WriteLine($"WaitTimeAsync finsih loopCount的值是: {loopCount}"); }
我們這時還可能改需求,需要在程式啟動5秒後,接下來4秒,再接下來3秒,列印loopCount,也就是上面的邏輯中間再插入一個3秒等待。
private static void WaitTimeAsync(int waitTime, Action action) { Thread thread = new Thread(()=>WaitTime(waitTime, action)); thread.Start(); } private static void WaitTimeFinishCallback() { Console.WriteLine($"WaitTimeAsync finsih loopCount的值是: {loopCount}"); WaitTimeAsync(4000, WaitTimeFinishCallback3); } private static void WaitTimeFinishCallback3() { Console.WriteLine($"WaitTimeAsync finsih loopCount的值是: {loopCount}"); WaitTimeAsync(3000, WaitTimeFinishCallback2); } private static void WaitTimeFinishCallback2() { Console.WriteLine($"WaitTimeAsync finsih loopCount的值是: {loopCount}"); }
這樣中間插入一段代碼,顯得非常麻煩。這裡可以回答什麼是協程了,其實這一串串回調就是協程。
ET開源地址地址:egametang/ET: Unity3D Client And C# Server Framework (github.com) qq群:474643097