2.2、更好的協程(ETBook)

上文講了一串回調就是協程,顯然這樣寫代碼,增加邏輯,插入邏輯非常容易出錯。我們需要利用異步語法把這個異步回調的形式改成同步的形式,幸好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原址:https://github.com/egametang/ET

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章