为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
九州编程 2021-12-05 16:49:34
我有两个最终都会更新同一个对象的服务,因此我们进行了测试以确保对该对象的写入完成(在幕后我们对每个服务都有重试策略)。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);        }    }}
查看完整描述

2 回答

?
萧十郎

TA贡献1815条经验 获得超13个赞

这里不需要 Tasks 和 Parrallel 循环。我假设你的_api调用是 IO 绑定的?你想要更像这样的东西:


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

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


//fire off all the async tasks

foreach(var it in iterations){

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

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

}


//await the results

await Task.WhenAll(tasksA).ConfigureAwait(false);


foreach (var task in tasksA)

{

    //no need to get GetAwaiter(), you've awaited above.

    task.Result;


//to get the most out of the async only await them just before you need them

await Task.WhenAll(tasksB).ConfigureAwait(false);


foreach (var task2 in tasksB)

{

     task2.Result;

}

这将触发您所有的 api 调用,async然后在结果返回时阻塞。您 Parallel for 和 tasks 只是使用额外的线程池线程来实现零收益。


如果_api受 CPU 限制,您可以从中受益,Task.Run但我猜这些是 web api 或其他东西。所以Task.Run除了使用额外的线程之外什么都不做。


查看完整回答
反对 回复 2021-12-05
?
哔哔one

TA贡献1854条经验 获得超8个赞

正如其他人所建议的那样,删除Parallel, 和await在asserting他们之前完成的所有任务。


我还建议.Result从每个任务中删除,而await不是它们。


public async Task ConcurrencyIssueTest(int iterations)

{

    var orderResult = await _driver.PlaceOrder();


    var taskA = _Api.TaskA(orderResult.OrderId);

    var taskB = _Api.TaskB(orderResult.OrderId);


    await Task.WhenAll(taskA, taskB);


    var taskAResult = await taskA;


    taskAResult.ShouldNotBeNull();

    taskAResult.StatusCode.ShouldBe(HttpStatusCode.OK);


    var taskBResult = await taskB;


    taskBResult.ShouldNotBeNull();

    taskBResult.StatusCode.ShouldBe(HttpStatusCode.OK);

}


查看完整回答
反对 回复 2021-12-05
  • 2 回答
  • 0 关注
  • 147 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信