使用 Task.Run 和 Parallel.For 的任务 null

我有两个最终都会更新同一个对象的服务,因此我们进行了测试以确保对该对象的写入完成(在幕后我们对每个服务都有重试策略)。


10 次中有 9 次,一个或多个理论将失败,并且task.ShouldNotBeNull();始终是失败的断言。这个示例中的异步代码有什么问题?为什么任务会是空的?


    [Theory]

    [InlineData(1)]

    [InlineData(5)]

    [InlineData(10)]

    [InlineData(20)]

    public async Task ConcurrencyIssueTest(int iterations)

    {

        var orderResult = await _driver.PlaceOrder();


        var tasksA = new List<Task<ApiResponse<string>>>();

        var tasksB = new List<Task<ApiResponse<string>>>();


        await Task.Run(() => Parallel.For(1, iterations,

            x =>

            {

                tasksA.Add(_Api.TaskA(orderResult.OrderId));

                tasksB.Add(_Api.TaskB(orderResult.OrderId));

            }));


        //Check all tasks return successful           

        foreach (var task in tasksA)

        {

            task.ShouldNotBeNull();


            var result = task.GetAwaiter().GetResult();


            result.ShouldNotBeNull();

            result.StatusCode.ShouldBe(HttpStatusCode.OK);

        }


         foreach (var task in tasksB)

        {

            task.ShouldNotBeNull();


            var result = task.GetAwaiter().GetResult();


            result.ShouldNotBeNull();

            result.StatusCode.ShouldBe(HttpStatusCode.OK);

        }


    }

}


九州编程
浏览 148回答 2
2回答

萧十郎

这里不需要 Tasks 和 Parrallel 循环。我假设你的_api调用是 IO 绑定的?你想要更像这样的东西:var tasksA = new List<Task<ApiResponse<string>>>();var tasksB = new List<Task<ApiResponse<string>>>();//fire off all the async tasksforeach(var it in iterations){&nbsp; &nbsp;tasksA.Add(_Api.TaskA(orderResult.OrderId));&nbsp; &nbsp;tasksB.Add(_Api.TaskB(orderResult.OrderId));}//await the resultsawait Task.WhenAll(tasksA).ConfigureAwait(false);foreach (var task in tasksA){&nbsp; &nbsp; //no need to get GetAwaiter(), you've awaited above.&nbsp; &nbsp; task.Result;}&nbsp;//to get the most out of the async only await them just before you need themawait Task.WhenAll(tasksB).ConfigureAwait(false);foreach (var task2 in tasksB){&nbsp; &nbsp; &nbsp;task2.Result;}这将触发您所有的 api 调用,async然后在结果返回时阻塞。您 Parallel for 和 tasks 只是使用额外的线程池线程来实现零收益。如果_api受 CPU 限制,您可以从中受益,Task.Run但我猜这些是 web api 或其他东西。所以Task.Run除了使用额外的线程之外什么都不做。

哔哔one

正如其他人所建议的那样,删除Parallel, 和await在asserting他们之前完成的所有任务。我还建议.Result从每个任务中删除,而await不是它们。public async Task ConcurrencyIssueTest(int iterations){&nbsp; &nbsp; var orderResult = await _driver.PlaceOrder();&nbsp; &nbsp; var taskA = _Api.TaskA(orderResult.OrderId);&nbsp; &nbsp; var taskB = _Api.TaskB(orderResult.OrderId);&nbsp; &nbsp; await Task.WhenAll(taskA, taskB);&nbsp; &nbsp; var taskAResult = await taskA;&nbsp; &nbsp; taskAResult.ShouldNotBeNull();&nbsp; &nbsp; taskAResult.StatusCode.ShouldBe(HttpStatusCode.OK);&nbsp; &nbsp; var taskBResult = await taskB;&nbsp; &nbsp; taskBResult.ShouldNotBeNull();&nbsp; &nbsp; taskBResult.StatusCode.ShouldBe(HttpStatusCode.OK);}
打开App,查看更多内容
随时随地看视频慕课网APP