【问题标题】:Execute all tasks simultaneously and await their completion?同时执行所有任务并等待它们完成?
【发布时间】:2015-10-06 00:28:43
【问题描述】:

我有一系列想要同时执行的异步方法。这些方法中的每一个都返回 true 或 false 来判断它们是否成功执行。他们的结果也会记录在我们的审计跟踪中,以便我们诊断问题。

我的一些函数并不依赖于所有这些方法的成功执行,我们完全预计其中一些函数会不时失败。如果确实失败了,程序将继续执行,它只会提醒我们的支持人员他们需要纠正问题。

我试图找出同时执行所有这些函数的最佳方法,但只有在它们都开始执行后才让父函数等待它们。如果任何一个函数失败,父函数将返回False,这将提醒我的应用程序停止执行。

我的想法是做类似的事情:

public async Task<bool> SetupAccessControl(int objectTypeId, int objectId, int? organizationId)
{
    using (var context = new SupportContext(CustomerId))
    {
        if (organizationId == null)
        {
            var defaultOrganization = context.Organizations.FirstOrDefault(n => n.Default);
            if (defaultOrganization != null)  organizationId = defaultOrganization.Id;
        }
    }

    var acLink = AcLinkObjectToOrganiation(organizationId??0,objectTypeId,objectId);

    var eAdmin = GrantRoleAccessToObject("eAdmin", objectTypeId, objectId);
    var defaultRole = GrantRoleAccessToObject("Default", objectTypeId, objectId);

    await acLink;
    await eAdmin;
    await defaultRole;

    var userAccess = (objectTypeId != (int)ObjectType.User) || await SetupUserAccessControl(objectId);

    return acLink.Result && eAdmin.Result && defaultRole.Result && userAccess;
}

public async Task<bool> SetupUserAccessControl(int objectId)
{
    var everyoneRole = AddToUserRoleBridge("Everyone", objectId);
    var defaultRole = AddToUserRoleBridge("Default", objectId);

    await everyoneRole;
    await defaultRole;

    return everyoneRole.Result && defaultRole.Result;
}

还有更好的选择吗?我应该以任何方式重组吗?我只是想加快执行时间,因为我有一个父函数可以执行近 20 个彼此独立的其他函数。即使在最慢的情况下,没有异步,它也只需要大约 1-2 秒来执行。但是,这将被扩展以最终使该父调用执行数百次(批量插入)。

【问题讨论】:

  • 我曾经使用 Parallel.Invoke 来处理这些东西。
  • 你可能想看看Task.WhenAll。对于结果值,如果不知道所涉及的一半类型,就很难判断...
  • 幸运的是,如果我不需要将值传递给父级(或等待它们在另一个函数中使用),所有函数都将返回 true 或 false 值。

标签: c# .net asynchronous async-await task-parallel-library


【解决方案1】:

异步方法有一个同步部分,它是在到达未完成任务的第一个等待之前的部分(如果没有,则整个方法同步运行)。该部分使用调用线程同步执行。

如果您想在不并行化这些部分的情况下同时运行这些方法,只需调用这些方法,收集任务并使用Task.WhenAll 立即等待所有这些。当所有任务完成后,您可以查看各个结果:

async Task<bool> SetupUserAccessControlAsync(int objectId)
{
    var everyoneRoleTask = AddToUserRoleBridgeAsync("Everyone", objectId);
    var defaultRoleTask = AddToUserRoleBridgeAsync("Default", objectId);

    await Task.WhenAll(everyoneRoleTask, defaultRoleTask)

    return await everyoneRoleTask && await defaultRoleTask;
}

如果您确实想并行化该同步部分,则需要多个线程,因此不要简单地调用异步方法,而是使用 Task.Run 卸载到 ThreadPool 线程:

async Task<bool> SetupUserAccessControlAsync(int objectId)
{
    var everyoneRoleTask = Task.Run(() => AddToUserRoleBridgeAsync("Everyone", objectId));
    var defaultRoleTask = Task.Run(() => AddToUserRoleBridgeAsync("Default", objectId));

    await Task.WhenAll(everyoneRoleTask, defaultRoleTask)

    return await everyoneRoleTask && await defaultRoleTask;
}

如果你的所有方法都返回bool,你可以将所有任务收集在一个列表中,从Task.WhenAll获取结果并检查是否全部返回true

async Task<bool> SetupUserAccessControlAsync(int objectId)
{
    var tasks = new List<Task<bool>>();
    tasks.Add(AddToUserRoleBridgeAsync("Everyone", objectId));
    tasks.Add(AddToUserRoleBridgeAsync("Default", objectId));

    return (await Task.WhenAll(tasks)).All(_ => _);
}

【讨论】:

  • 我不担心它们完全并发执行,我只是想确保每个都尽快执行,而不会阻塞下一次调用的执行。
  • @Jdsfighter 这些句子相互矛盾。如果您希望第二个操作尽快开始,那么您需要第一个操作尽快释放调用线程。这意味着甚至将同步部分卸载到不同的线程。
  • 每个调用都执行一系列数据库功能。我只是想确保我可以快速执行这些功能,而不会长时间阻塞调用线程(就像同步数据库调用那样)。
  • @Jdsfighter 如果您不使用Task.Run,调用线程将一一运行这些同步部分。如果那是延长的时间取决于您的代码。 Task.Run 将这些部分卸载到 ThreadPool 线程,因此调用线程尽可能少地执行。
  • 我终于起身去和我们的一位高级开发人员交谈,他终于帮助我掌握了一点。似乎我经常将异步和并行性混为一谈。他还提到我可能应该避免将操作放在单独的线程中,除非它们相当占用 CPU。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-21
相关资源
最近更新 更多