【问题标题】:Rewrite multi-thread wait using async/await使用 async/await 重写多线程等待
【发布时间】:2012-09-24 17:57:45
【问题描述】:

我们有一个在后台运行任务列表的网络服务功能。完成所有任务后,它会返回。我重新审视了 C# 5 的函数。我设法让以下 sn-p 工作:

var result = new List<int>();
var tasks = new List<Task<int>>();

foreach (var row in rowlist)
    tasks.Add(Task.Factory.StartNew(() => DoWork(row)));
foreach (var task in tasks)
    result.Add(task.Result);

task.Result 等待单个任务完成。我已经通过向DoWork 方法添加三秒延迟并验证它在不到 5 秒的时间内完成了 10 个任务的列表来对此进行了测试。

现在我尝试使用新的async / await 语法重写函数。但我似乎无法让它工作。如果它编译,它会同步运行,通过添加等待来验证。

关于如何使用async / await 重写上述 sn-p 有什么想法吗?

【问题讨论】:

    标签: c# multithreading async-await c#-5.0


    【解决方案1】:

    我会这样写:

    var tasks = rowlist.Select(row => Task.Run(() => DoWork(row)));
    
    var results = (await Task.WhenAll(tasks)).ToList();
    

    基本上,您仍然按照以前的方式创建任务(不使用await),因为您希望它们都立即开始。然后,您可以等待它们全部异步完成(通过await Task.WhenAll),然后立即提取结果(因为您知道它们都已完成)。

    【讨论】:

    • 这是异步运行的。但它会导致System.AggregateException,内部异常{"The query results cannot be enumerated more than once."} 任何想法?
    • 也许更重要的是:如果省略await 行,为什么会同步运行?
    • @Andomar 在创建任务时在Select 之后调用ToList。那应该可以解决错误。目前,当多次枚举tasks 时,它会尝试创建两次新任务,这会很糟糕。
    • @Andomar 它同步运行,因为如果任务尚未完成,Result 将阻塞直到任务完成。 await 做了一大堆魔法来使方法的其余部分同步运行。一旦到达await,它将立即结束方法调用。当WhenAll 完成时(即,当所有其他任务都完成时)它将导致一个延续触发(await 创建并附加该延续);该延续将具有方法的其余部分(即获取结果的行)。
    • 你可以稍微简化一下:var results = await Task.WhenAll(tasks);
    【解决方案2】:

    根据 Reed Copsey、Servy 和 Stephen Cleary 的回答和 cmets,我已经发展到这个解决方案:

    var results = rowlist
        .Select(row => Task.Run(() => DoWork(row)))
        .ToList()
        .Select(t => t.Result);
    

    第二个Select 将等待任务完成。我在两者之间添加了ToList() 以防止流式传输,但没有它似乎也可以工作。

    【讨论】:

    • 这与您开始使用的代码基本相同。你需要那里的await...
    • @Servy:现在我很困惑:不是你在上面评论task.Result 也可以阻止吗?顺便说一句,代码确实有效:4 秒内执行 10 个任务,其中每个任务休眠 3 秒。
    • 使用 async/await 的重点是整个方法(此代码所在的)不会阻塞。例如,您可以从 GUI 应用程序中的一个事件调用这些方法,而不是让它阻塞 UI 线程(同时仍在 UI 线程中运行所有需要的代码)。使用await,该方法实际上并不返回结果,而是返回一个任务,该任务会在计算出这些结果时通知您(以及这些结果可能是什么)。
    • @Servy:await 不会阻塞 GUI 线程吗?我明白你的意思,但这里的要求只是以可读的方式并行化任务。它肯定比使用ManualResetEvent 对象列表的旧方法更好。
    • 不,如果您使用await,您将不会阻塞 UI 线程。这就是它的伟大之处。你可以编写看起来是同步的代码,就像你想象的那样,但它会做一大堆魔法,将它转换成一个防止线程被阻塞的延续/回调模型。至于要求,问题特别要求重写代码以使用async/await,因此排除它不符合[您的]要求。
    猜你喜欢
    • 1970-01-01
    • 2021-12-11
    • 2016-03-08
    • 1970-01-01
    • 1970-01-01
    • 2022-11-07
    • 2020-11-19
    • 1970-01-01
    • 2013-02-22
    相关资源
    最近更新 更多