更好的協程 上文講了一串回調就是協程,顯然這樣寫代碼,增加邏輯,插入邏輯非常容易出錯。我們需要利用非同步語法把這個非同步回調的形式改成同步的形式,幸好C#已經幫我們設計好了,看代碼 // example2_2 class Program { private static int loopCount = ...
更好的協程
上文講了一串回調就是協程,顯然這樣寫代碼,增加邏輯,插入邏輯非常容易出錯。我們需要利用非同步語法把這個非同步回調的形式改成同步的形式,幸好C#已經幫我們設計好了,看代碼
// example2_2 class Program { private static int loopCount = 0; static void Main(string[] args) { OneThreadSynchronizationContext _ = OneThreadSynchronizationContext.Instance; Console.WriteLine($"主線程: {Thread.CurrentThread.ManagedThreadId}"); Crontine(); while (true) { OneThreadSynchronizationContext.Instance.Update(); Thread.Sleep(1); ++loopCount; if (loopCount % 10000 == 0) { Console.WriteLine($"loop count: {loopCount}"); } } } private static async void Crontine() { await WaitTimeAsync(5000); Console.WriteLine($"當前線程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}"); await WaitTimeAsync(4000); Console.WriteLine($"當前線程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}"); await WaitTimeAsync(3000); Console.WriteLine($"當前線程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}"); } private static Task WaitTimeAsync(int waitTime) { TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(); Thread thread = new Thread(()=>WaitTime(waitTime, tcs)); thread.Start(); return tcs.Task; } /// <summary> /// 在另外的線程等待 /// </summary> private static void WaitTime(int waitTime, TaskCompletionSource<bool> tcs) { Thread.Sleep(waitTime); // 將tcs扔回主線程執行 OneThreadSynchronizationContext.Instance.Post(o=>tcs.SetResult(true), null); } }
在這段代碼裡面,WaitTimeAsync方法中,我們利用了TaskCompletionSource類替代了之前傳入的Action參數,WaitTimeAsync方法返回了一個Task類型的結果。WaitTime中我們把action()替換成了tcs.SetResult(true),WaitTimeAsync方法前使用await關鍵字,這樣可以將一連串的回調改成同步的形式。這樣一來代碼顯得十分簡潔,開發起來也方便多了。
這裡還有個技巧,我們發現WaitTime中需要將tcs.SetResult扔回到主線程執行,微軟給我們提供了一種簡單的方法,參考example2_2_2,在主線程設置好同步上下文,
// example2_2_2 SynchronizationContext.SetSynchronizationContext(OneThreadSynchronizationContext.Instance);
在WaitTime中直接調用tcs.SetResult(true)就行了,回調會自動扔到同步上下文中,而同步上下文我們可以在主線程中取出回調執行,這樣自動能夠完成回到主線程的操作
private static void WaitTime(int waitTime, TaskCompletionSource<bool> tcs) { Thread.Sleep(waitTime); tcs.SetResult(true); }
如果不設置同步上下文,你會發現列印出來當前線程就不是主線程了,這也是很多第三方庫跟.net core內置庫的用法,預設不回調到主線程,所以我們使用的時候需要設置下同步上下文。其實這個設計本人覺得沒有必要,交由庫的開發者去實現更好,尤其是在游戲開發中,邏輯全部是單線程的,回調每次都走一遍同步上下文就顯得多餘了,所以ET框架提供了不使用同步上下文的實現ETTask,代碼更加簡潔更加高效,這個後面會講到。
ET開源地址地址:egametang/ET: Unity3D Client And C# Server Framework (github.com) qq群:474643097