gpt4 book ai didi

C# Chained ContinueWith 不等待上一个任务完成

转载 作者:行者123 更新时间:2023-11-30 15:17:29 26 4
gpt4 key购买 nike

我正在测试 C# async/await 的异步性,并意外发现 ContinueWith 的后续代码不等待前一个任务完成:

public async Task<int> SampleAsyncMethodAsync(int number,string id)
{
Console.WriteLine($"Started work for {id}.{number}");
ConcurrentBag<int> abc = new ConcurrentBag<int>();

await Task.Run(() => { for (int count = 0; count < 30; count++) { Console.WriteLine($"[{id}] Run: {number}"); abc.Add(count); } });

Console.WriteLine($"Completed work for {id}.{number}");
return abc.Sum();
}

使用以下测试方法执行:

        [Test]
public void TestAsyncWaitForPreviousTask()
{
for (int count = 0; count < 3; count++)
{
int scopeCount = count;

var c = SampleAsyncMethodAsync(0, scopeCount.ToString())
.ContinueWith((prevTask) =>
{
return SampleAsyncMethodAsync(1, scopeCount.ToString());
})
.ContinueWith((prevTask2) =>
{
return SampleAsyncMethodAsync(2, scopeCount.ToString());
});
}
}

输出显示运行 0.0、1.0 和 2.0 的执行异步正确执行,但随后的 x.1 和 x.2 几乎立即开始并且 x.2 实际上在 x.1 之前完成。例如。如下所示:

[2] Run: 0
[2] Run: 0
[2] Run: 0
Completed work for 2.0
Started work for 0.1
Started work for 0.2 <-- surprise!
[0] Run: 2
[0] Run: 2
[0] Run: 2
[0] Run: 2
[0] Run: 2

似乎 continueWith 只会等待第一个任务 (0) 而不管后续链。我可以通过在第一个 Continuewith block 中嵌套第二个 ContinueWith 来解决问题。

我的代码有问题吗?我假设 Console.WriteLine 遵循 FIFO。

最佳答案

简而言之,你期望ContinueWith等待先前返回的对象。在 Task 中返回一个对象(甚至是 ContinueWith ) action 对返回值不做任何事情,它不等待它完成,它返回它并传递给继续(如果存在)。

确实会发生以下事情:

  • 你跑 SampleAsyncMethodAsync(0, scopeCount.ToString())
  • 完成后,执行继续 1:

    return SampleAsyncMethodAsync(1, scopeCount.ToString());

    当它偶然发现 await Task.Run 时,它返回一个任务。即,它不会等待 SampleAsyncMethodAsync 完成。

  • 然后,continuation 1 被认为已经完成,因为它返回了一个值(任务)
  • 继续 2 运行。

如果您手动等待每个异步方法,那么它将依次运行:

for (int count = 0; count < 3; count++)
{
int scopeCount = count;

var c = SampleAsyncMethodAsync(0, scopeCount.ToString())
.ContinueWith((prevTask) =>
{
SampleAsyncMethodAsync(1, scopeCount.ToString()).Wait();
})
.ContinueWith((prevTask2) =>
{
SampleAsyncMethodAsync(2, scopeCount.ToString()).Wait();
});
}

使用 ContinueWith(async t => await SampleAsyncMethodAsync...也不起作用,因为它会导致包装 Task<Task>结果(很好地解释了 here )。

此外,您还可以执行以下操作:

for (int count = 0; count < 3; count++)
{
int scopeCount = count;

var c = SampleAsyncMethodAsync(0, scopeCount.ToString())
.ContinueWith((prevTask) =>
{
SampleAsyncMethodAsync(1, scopeCount.ToString())
.ContinueWith((prevTask2) =>
{
SampleAsyncMethodAsync(2, scopeCount.ToString());
});
});
}

但是,它创建了某种回调 hell 并且看起来很乱。

您可以使用 await使这段代码更简洁:

for (int count = 0; count < 3; count++)
{
int scopeCount = count;

var d = Task.Run(async () => {
await SampleAsyncMethodAsync(0, scopeCount.ToString());
await SampleAsyncMethodAsync(1, scopeCount.ToString());
await SampleAsyncMethodAsync(2, scopeCount.ToString());
});
}

现在,它针对 3 个计数运行 3 个任务,因此每个任务将运行带有 number 的异步方法。等于 1、2 和 3。

关于C# Chained ContinueWith 不等待上一个任务完成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46235216/

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