TH1/Book/2.2更好的协程.md
2025-07-17 18:26:28 +08:00

77 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 更好的协程
上文讲了一串回调就是协程显然这样写代码增加逻辑插入逻辑非常容易出错。我们需要利用异步语法把这个异步回调的形式改成同步的形式幸好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代码更加简洁更加高效这个后面会讲到。