2.3、單線程異步(ETBook)

前面幾個例子都是多線程實現的異步,但是異步顯然不僅僅是多線程的。我們在之前的例子中使用了Sleep來實現時間的等待,每一個計時器都需要使用一個線程,會導致線程切換頻繁,這個實現效率很低,平常是不會這樣做的。一般遊戲邏輯中會設計一個單線程的計時器,我們這裏做一個簡單的實現,用來講解單線程異步。

    // example2_3
    class Program
    {
        private static int loopCount = 0;

        private static long time;
        private static Action action;
        
        static void Main(string[] args)
        {
            Console.WriteLine($"主線程: {Thread.CurrentThread.ManagedThreadId}");

            Crontine();
            
            while (true)
            {
                Thread.Sleep(1);

                CheckTimerOut();
                
                ++loopCount;
                if (loopCount % 10000 == 0)
                {
                    Console.WriteLine($"loop count: {loopCount}");
                }
            }
        }
        
        private static void Crontine()
        {
            WaitTimeAsync(5000, WaitTimeAsyncCallback1);
        }

        private static void WaitTimeAsyncCallback1()
        {
            Console.WriteLine($"當前線程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
            WaitTimeAsync(4000, WaitTimeAsyncCallback2);
        }
        
        private static void WaitTimeAsyncCallback2()
        {
            Console.WriteLine($"當前線程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
            WaitTimeAsync(3000, WaitTimeAsyncCallback3);
        }
        
        private static void WaitTimeAsyncCallback3()
        {
            Console.WriteLine($"當前線程: {Thread.CurrentThread.ManagedThreadId}, WaitTimeAsync finsih loopCount的值是: {loopCount}");
        }

        private static void CheckTimerOut()
        {
            if (time == 0)
            {
                return;
            }
            long nowTicks = DateTime.Now.Ticks / 10000;
            if (time > nowTicks)
            {
                return;
            }

            time = 0;
            action.Invoke();
        }
        
        private static void WaitTimeAsync(int waitTime, Action a)
        {
            time = DateTime.Now.Ticks / 10000 + waitTime;
            action = a;
        }
    }

這個例子同樣實現了一個簡單的計時方法,WaitTimeAsync調用時會將回調方法跟時間記錄下來,主線程每幀都會調用CheckTimerOut,CheckTimerOut裏面判斷計時器是否過期,過期了則調用回調方法。整個邏輯都在主線程中完成,同樣是異步方法。所以異步並非多線程,單線程同樣可以異步。上面的例子同樣可以改成await的形式。

    // example2_3_2
    class Program
    {
        private static int loopCount = 0;

        private static long time;
        private static TaskCompletionSource<bool> tcs;
        
        static void Main(string[] args)
        {
            Console.WriteLine($"主線程: {Thread.CurrentThread.ManagedThreadId}");

            Crontine();
            
            while (true)
            {
                Thread.Sleep(1);

                CheckTimerOut();
                
                ++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 void CheckTimerOut()
        {
            if (time == 0)
            {
                return;
            }
            long nowTicks = DateTime.Now.Ticks / 10000;
            if (time > nowTicks)
            {
                return;
            }

            time = 0;
            tcs.SetResult(true);
        }
        
        private static Task WaitTimeAsync(int waitTime)
        {
            TaskCompletionSource<bool> t = new TaskCompletionSource<bool>();
            time = DateTime.Now.Ticks / 10000 + waitTime;
            tcs = t;
            return t.Task;
        }
    }

上面這個例子所有調用全部在主線程中完成,並且使用了await,因此await並不會開啓多線程,await具體用沒用多線程完全取決與具體的實現

ET原址:https://github.com/egametang/ET

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