gpt4 book ai didi

c# - 异步和等待 - 如何维护执行顺序?

转载 作者:太空狗 更新时间:2023-10-29 18:04:32 24 4
gpt4 key购买 nike

我实际上正在阅读一些关于任务并行库和使用 async 和 await 进行异步编程的主题。 《C# 5.0 in a Nutshell》一书指出,当使用 await 关键字等待表达式时,编译器会将代码转换为如下内容:

var awaiter = expression.GetAwaiter();
awaiter.OnCompleted (() =>
{
var result = awaiter.GetResult();

假设我们有这个异步函数(也来自引用书):

async Task DisplayPrimeCounts()
{
for (int i = 0; i < 10; i++)
Console.WriteLine (await GetPrimesCountAsync (i*1000000 + 2, 1000000) +
" primes between " + (i*1000000) + " and " + ((i+1)*1000000-1));
Console.WriteLine ("Done!");
}

“GetPrimesCountAsync”方法的调用将排队并在池线程上执行。一般来说,从 for 循环中调用多个线程有可能引入竞争条件。

那么 CLR 如何确保请求将按照它们发出的顺序进行处理?我怀疑编译器只是将代码转换为上述方式,因为这会将“GetPrimesCountAsync”方法与 for 循环分离。

最佳答案

为了简单起见,我将用一个稍微简单但具有所有相同有意义属性的示例替换您的示例:

async Task DisplayPrimeCounts()
{
for (int i = 0; i < 10; i++)
{
var value = await SomeExpensiveComputation(i);
Console.WriteLine(value);
}
Console.WriteLine("Done!");
}

由于您的代码定义,所有顺序都得到维护。让我们想象一下逐步完成它。

  1. 这个方法首先被调用
  2. 第一行代码是for循环,所以i已初始化。
  3. 循环检查通过,所以我们进入循环体。
  4. SomeExpensiveComputation叫做。它应该返回 Task<T>很快,但它所做的工作将在后台继续进行。
  5. 方法的其余部分作为返回任务的延续添加;该任务完成后它将继续执行。
  6. 任务从SomeExpensiveComputation返回后完成后,我们将结果存储在 value 中.
  7. value打印到控制台。
  8. 转到 3;请注意,在我们第二次进入第 4 步并开始下一个操作之前,现有的昂贵操作已经完成。

就 C# 编译器实际完成第 5 步的方式而言,它是通过创建状态机来实现的。基本上每次都有一个await有一个标签指示它停止的位置,并且在方法开始时(或在任何延续触发后恢复之后)它检查当前状态,并执行 goto。到它停止的地方。它还需要将所有局部变量提升到新类的字段中,以便维护这些局部变量的状态。

现在这种转换实际上并不是在 C# 代码中完成的,而是在 IL 中完成的,但这在某种程度上相当于我在状态机中显示的代码的士气。请注意,这不是有效的 C#(您不能 goto 像这样进入 for 循环,但该限制不适用于实际使用的 IL 代码。这和什么之间也会有差异C# 实际上可以,但是应该让您对这里发生的事情有一个基本的了解:

internal class Foo
{
public int i;
public long value;
private int state = 0;
private Task<int> task;
int result0;
public Task Bar()
{
var tcs = new TaskCompletionSource<object>();
Action continuation = null;
continuation = () =>
{
try
{
if (state == 1)
{
goto state1;
}
for (i = 0; i < 10; i++)
{
Task<int> task = SomeExpensiveComputation(i);
var awaiter = task.GetAwaiter();
if (!awaiter.IsCompleted)
{
awaiter.OnCompleted(() =>
{
result0 = awaiter.GetResult();
continuation();
});
state = 1;
return;
}
else
{
result0 = awaiter.GetResult();
}
state1:
Console.WriteLine(value);
}
Console.WriteLine("Done!");
tcs.SetResult(true);
}
catch (Exception e)
{
tcs.SetException(e);
}
};
continuation();
}
}

请注意,为了这个示例,我忽略了任务取消,我忽略了捕获当前同步上下文的整个概念,还有更多错误处理等。不要认为这是一个完成实现。

关于c# - 异步和等待 - 如何维护执行顺序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31705405/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com