C#Chaining继续不等待以前的任务完成



我正在测试c#async/等待的异步性,并遇到了一个惊喜,在此过程中,随后的继续执行的代码不会等待上一个任务完成:

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

似乎仍然只能等待第一个任务(0),而不管随后的链条如何。我可以通过将第二个继续嵌套在第一个继续块中来解决问题。

我的代码有问题吗?我假设Console.Writeline尊重FIFO。

简而言之,您希望ContinueWith等待先前返回的对象。在ContinueWith操作中返回对象(甚至是Task),对返回的值无能为力,它不会等待它完成,它将其返回并在持续情况下返回。

确实发生了以下事情:

  • 您运行SampleAsyncMethodAsync(0, scopeCount.ToString())
  • 完成后,您执行延续1:

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

    并且当它偶然发现await Task.Run时,它会返回任务。即,它不会等待采样的spemencmethodasync完成。

  • 然后,由于它返回了一个值(任务)
  • ,延续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>结果(在此处解释很好)。

另外,您可以做类似:

的事情
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());
                });
        });   
}

但是,它创造了某种回调地狱,看起来很混乱。

您可以使用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的异步方法运行。

相关内容

  • 没有找到相关文章

最新更新