【问题标题】:Async/Await, Multiple Tasks (.NET 4.0/ NuGet Microsoft Async)异步/等待,多任务 (.NET 4.0/ NuGet Microsoft Async)
【发布时间】:2014-08-14 19:07:50
【问题描述】:

我仅限于 Microsoft Async NuGet 包,到目前为止我真的很喜欢 async/await 语法。我有很多方法可以顺利地使用这种技术,但是我遇到了一些问题,试图变得更高级。

我需要加载多个数据源、库存、部门等。我希望顶层方法等待所有源加载,然后继续。代码似乎陷入僵局。我已经做了一些研究,但我的理解中缺少一些东西。

这是顶级调用:

await GlobalData.WaitAsync(GlobalData.DataType.Inventory | GlobalData.DataType.Departments).ConfigureAwait(continueOnCapturedContext: false);

WaitAsync..

public static async Task<bool> WaitAsync(DataType flags)
{
    TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
    Task<bool> task = tcs.Task;

    ThreadPool.QueueUserWorkItem(_ =>
    {

        try
        {
            List<Task> tasks = new List<Task>();

            foreach (DataType t in Enum.GetValues(typeof(DataType)))
            {
                if (t != DataType.None && flags.HasFlag(t))
                {
                    tasks.Add(GetModel(t).GetTask());
                }
            }

            /* Remove any tasks that are null. */
            tasks.RemoveAll(t => t == null);

            Task.WaitAll(tasks.ToArray());
            tcs.SetResult(true);
        }
        catch (Exception)
        {
            throw;
        }
    });

    return await task;

最后,在 GetTask(..) 内部

public override Task GetTask()
{
    /* If the task is not null, another caller has asked for the data already, hook into the callback. */
    if (m_LoadingTask != null)
    {
        return m_LoadingTask;
    }
    else
    {
        /* Only load if the bindingList is null, return the Task. */
        if (m_Data == null)
        {
            return this.LoadAsync();
        }
    }

    return null;
}

我可以看到代码按预期执行。 Task.WaitAll(...) 返回并执行 tcs.SetResult(true),但永远不会回到顶层 await。

我能做些什么来解决这个问题?

提前致谢。

【问题讨论】:

  • 为什么你的 WaitAsync 方法完全是异步方法?您可以只从同步方法返回tcs.Task...以return await ... 结尾并且没有任何其他await 表达式的异步方法始终至少是重构的候选。这不是这里的问题,而是需要考虑的问题。 (同样,任何时候你有一个只是 throw; 的 catch 块,考虑删除它......唯一的好处是能够在它上面放置一个断点。)
  • 您好 Jon,感谢您的及时回复。您对我使用 async/await 的观察是正确的。看来我错过了重点。我将使用 TAP 模型进行重构,并将 aysnc/await 仅应用于我的顶级方法。我会回复结果。谢谢!
  • 明确地说,我不希望它改变行为,只是简化代码。

标签: .net winforms asynchronous .net-4.0 async-await


【解决方案1】:

您不需要使用TaskCompletionSourceThreadPool.QueueUserWorkItem,只需await Task.WhenAll(tasks.ToArray()) 就可以了。

public static async Task<bool> WaitAsync(DataType flags)
{
    List<Task> tasks = new List<Task>();

    foreach (DataType t in Enum.GetValues(typeof(DataType)))
    {
      if (t != DataType.None && flags.HasFlag(t))
      {
          tasks.Add(GetModel(t).GetTask());
      }
    }

    /* Remove any tasks that are null. */
    tasks.RemoveAll(t => t == null);

    await TaskEx.WhenAll(tasks.ToArray());

    return true;
}

您可能会发生死锁,因为您使用了Task.WaitAllTask.WaitAllTask.WaitTask.Result 正在阻塞调用,可能导致死锁。请参阅this 文章了解更多信息。

对于 .Net 4.0 和 Async CTP,您可以使用 TaskEx.WhenAll

【讨论】:

  • 嗨,内德,Task.WhenAll(...) 在带有 NuGet Microsoft Async 的 .NET4.0 中不可用。我希望是!
  • 对于 .Net 4.0 和异步 CTP,您可以使用 TaskEx.WhenAll
  • 哇哦!你说的对。我肯定会把它添加到我的曲目中。对不起,我第一次读的时候错过了。正如 Jon Skeet 所说,我将删除 return await,因为它没有必要。此外,我希望代码阻止我的顶级方法。我将同时使用 Jon 的 Wait 和您的 WaitAsync。谢谢内德!
【解决方案2】:

感谢 Jon Skeet 为我指明了正确的方向。我删除了 async/await 并发现此代码是解决方案:

public static bool Wait(DataType flags)
{
    List<Task> tasks = new List<Task>();

    foreach (DataType t in Enum.GetValues(typeof(DataType)))
    {
        if (t != DataType.None && flags.HasFlag(t))
        {
            tasks.Add(GetModel(t).GetTask());
        }
    }

    /* Remove any tasks that are null. */
    tasks.RemoveAll(t => t == null);

    Task.WaitAll(tasks.ToArray());

    return true;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-18
    • 2023-03-24
    • 2014-09-06
    • 1970-01-01
    • 2013-02-10
    相关资源
    最近更新 更多