# 更好的协程
上文讲了一串回调就是协程，显然这样写代码，增加逻辑，插入逻辑非常容易出错。我们需要利用异步语法把这个异步回调的形式改成同步的形式，幸好C#已经帮我们设计好了，看代码
```csharp
    // 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，在主线程设置好同步上下文，
```csharp
// example2_2_2
SynchronizationContext.SetSynchronizationContext(OneThreadSynchronizationContext.Instance);
```
在WaitTime中直接调用tcs.SetResult(true)就行了，回调会自动扔到同步上下文中，而同步上下文我们可以在主线程中取出回调执行，这样自动能够完成回到主线程的操作
```csharp
        private static void WaitTime(int waitTime, TaskCompletionSource<bool> tcs)
        {
            Thread.Sleep(waitTime);

            tcs.SetResult(true);
        }
```
如果不设置同步上下文，你会发现打印出来当前线程就不是主线程了，这也是很多第三方库跟.net core内置库的用法，默认不回调到主线程，所以我们使用的时候需要设置下同步上下文。其实这个设计本人觉得没有必要，交由库的开发者去实现更好，尤其是在游戏开发中，逻辑全部是单线程的，回调每次都走一遍同步上下文就显得多余了，所以ET框架提供了不使用同步上下文的实现ETTask，代码更加简洁更加高效，这个后面会讲到。