【问题标题】:Using ContinueWith with Multiple Tasks对多个任务使用 ContinueWith
【发布时间】:2017-05-12 04:29:55
【问题描述】:

这并不像我想象的那么简单,需要启动许多任务来对一个对象进行操作。每个任务一个唯一的对象。第二部分是ContinueWith,当每个任务报告结果时。但是,我没有得到 WhenAll 类型的行为。希望有人能纠正我。

_tasks = new Task<AnalysisResultArgs>[_beansList.Count];
for (int loopCnt = 0; loopCnt < _beansList.Count; loopCnt++)
{
    _tasks[loopCnt] = Task<AnalysisResultArgs>.Factory.StartNew(() =>
    {
        return _beansList[loopCnt].Analyze(newBeanData);
    });
    await _tasks[loopCnt].ContinueWith(ReportResults, 
                  TaskContinuationOptions.RunContinuationsAsynchronously)  
    // do some housekeeping when all tasks are complete          
}

private void ReportResults(Task<AnalysisResultArgs> task)
{
     /* Do some serial operations
}

据我了解,_beansList.Count 任务将被启动,并且通过在 ContinueWith 上使用 await,在所有任务完成之前不会执行内务处理工作。我无法阻止,因为我需要确保能够限制传入数据以防止等待执行的任务过多。

我在哪里搞砸了,等待实际上完成了,即使不是所有的任务都已经完成,但仍会运行内务管理。

【问题讨论】:

  • 请简化为完整并重现问题的最小示例。旁注:如果你使用Task.Run 代替StartNew,使用await 代替ContinueWith,那么你的代码会更干净,表现更好。
  • Stephen,我感谢您的评论和您在这个主题上的公认权威,但是,这是一个最小的工作示例,减去了一些额外的类。您对 Task.Run 而不是 StartNew 和使用 await(我做过)而不是 ContinueWith 的声明,我会看看我是否可以在网上寻找为什么这样更好。我认为 ContinueWith 就是这个意思,而 await 就是它所说的意思。套用开始一个新任务并在任务完成时继续第二步。等到一切都结束。
  • 这个例子并不完整。我无法将其粘贴到新项目中并在调试器中观察其行为。我在 why StarNew is dangerouswhy ContinueWith is dangerous 上有博文。
  • @StephenCleary 这些博客我有好几次了,它们帮了大忙。不,您不能将其粘贴到项目中并逐步执行。我为此道歉。您的 cmets 和其他 cmets 仍然有很大帮助。

标签: c# async-await task-parallel-library


【解决方案1】:

您不是在等待所有任务,而是在等待循环中的继续。您应该为此使用Task.WhenAll 方法。另外,如果您可以在任务中运行它,为什么还需要继续?像这样简化你的代码:

private void ReportResults(AnalysisResultArgs results)
{
     /* Do some serial operations */
}

...
_tasks = new Task<AnalysisResultArgs>[_beansList.Count];
for (int loopCnt = 0; loopCnt < _beansList.Count; loopCnt++)
{
    var count = loopCnt;
    _tasks[count] = Task.Run(() =>
    {
        var results = _beansList[count].Analyze(newBeanData);
        ReportResults(results);
        return results;
    });
}

// do some housekeeping when all tasks are complete          
await Task.WhenAll(_tasks);

【讨论】:

  • 该死!你是绝对正确的。如此简单,就在我的鼻子面前。谢谢!非常感谢。
  • @AeroClassics 祝你的项目好运:)
  • 代码有两个问题,results需要返回,否则编译错误,因为填了Task&lt;AnalysisResultArgs&gt;[]loopCnt要赋值给一个局部变量否则会导致运行时索引放范围异常
【解决方案2】:

正如@Stephen 已经提到的,问题中的代码不是最少的、完整的和可验证的。我冒昧地做了一些假设,在我看来你的代码应该是这样的:

public async Task<AnalysisResultArgs[]> MainMethod()
{
    var _beansList = new List<AnalysisResultArgs>();

    for(int i=0; i< 99; i++) // Considering 100 records
        _beansList.Add(new AnalysisResultArgs());

    var _tasks = new Task<AnalysisResultArgs>[_beansList.Count];

    for (int loopCnt = 0; loopCnt < _beansList.Count; loopCnt++)
    {
        var local = loopCnt;
        _tasks[local] = Task.Run(async() => await ReportResults(_beansList[local].Analyze(new AnalysisResultArgs())));
    }

    return await Task.WhenAll(_tasks);
}

private async Task<AnalysisResultArgs> ReportResults(Task<AnalysisResultArgs> task)
{
    await Task.Delay(1000);
    return await Task.FromResult(new AnalysisResultArgs());
}

public class AnalysisResultArgs
{   
    public async Task<AnalysisResultArgs> Analyze(AnalysisResultArgs newBeanData)
    {
        await Task.Delay(1000);
        return await Task.FromResult(new AnalysisResultArgs());
    }
}

假设/其他细节:

  1. Task.DelayTask.FromResult 只是实际逻辑的占位符。
  2. 调用AnalyzeReportResults 中的所有方法都是异步的
  3. 循环中的代码可能是:

    _tasks[local] = ReportResults(_beansList[local].Analyze(new 
             AnalysisResultArgs()));
    

但是如果我们使用Task.Run 来启动Task,那么async, await 有助于释放调用线程池线程/同步上下文。它有助于提高系统的可扩展性。

  1. 假设_beansList的类型为List&lt;AnalysisResultArgs&gt;,但可以根据实际需要进行修改
  2. ReportResults 可以修改为采用Func&lt;Task&lt;AnalysisResultArgs&gt;&gt; func,而不是简单的 dTask&lt;AnalysisResultArgs&gt;,因此在方法内部执行await func(),然后调用代码可以真正异步如下:

      _tasks[local] = Task.Run(async() => await ReportResults(async() => await 
      _beansList[local].Analyze(new AnalysisResultArgs())));
    

【讨论】:

    猜你喜欢
    • 2018-02-02
    • 1970-01-01
    • 2011-05-23
    • 2023-03-23
    • 2019-07-15
    • 2013-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多