【问题标题】:Proper way to chain Tasks链接任务的正确方法
【发布时间】:2014-08-15 06:32:36
【问题描述】:

我想链Tasks,然后并行启动链。 这个 sn-p 只是为了说明我的问题:

        var taskOrig = new Task(() => { });
        var task = taskOrig;
        foreach (var msg in messages)
        {
            task=task.ContinueWith(t => Console.WriteLine(msg));
        }
        taskOrig.Start();

除了我内心的一个小完美主义者不喜欢先执行空方法() => { }之外,一切都很好。

有什么办法可以避免吗?

我确实了解它几乎不会影响性能(除非您经常这样做),但仍然如此。就我而言,性能很重要,因此检查任务是否存在于每次迭代中并不是这样做的方法。

【问题讨论】:

  • “在我的情况下,性能很重要,因此检查任务是否存在于每次迭代中并不是这样做的方法。”:与实际执行任务相比,它所花费的时间可以忽略不计。除非您确实测量了性能影响,否则这显然是过早优化的情况。
  • @ThomasLevesque 你可能是对的,我只是想也许我在Task 创建 API 时遗漏了一些东西。稍后我将有机会衡量性能影响。
  • 您可能会发现 TPL DataFlow 很有趣

标签: c# multithreading task-parallel-library


【解决方案1】:

你可以这样做:

Task task = Task.FromResult<object>(null);
foreach (var msg in messages)
{
    task = task.ContinueWith(t => Console.WriteLine(msg));
}

以前的解决方案在 4.0 中不起作用。在 4.0 中,您需要执行以下操作:

var tcs = new TaskCompletionSource<object>();
Task task = tcs.Task;
foreach (var msg in messages)
{
    task = task.ContinueWith(t => Console.WriteLine(msg));
}

tcs.SetResult(null);

(如果您愿意,可以将 SetResult 移到 foreach 循环之前。)

从技术上讲,当您仍在添加更多内容时,延续将开始执行并不相同。不过这不太可能是个问题。

另一种选择是使用这样的东西:

public static Task ForEachAsync<T>(IEnumerable<T> items, Action<T> action)
{
    return Task.Factory.StartNew(() =>
    {
        foreach (T item in items)
        {
            action(item);
        }
    });
}

一个示例用法是:

ForEachAsync(messages, msg => Console.WriteLine(msg));

【讨论】:

  • 注意:Task.FromResult() 是 .Net 4.5 中的新功能,在 .Net 4.0 中,您需要使用 TaskCompletionSource 手动执行此操作。
  • @svick 对,已添加解决方案。
  • 很好,thanx,Task.FromResult&lt;object&gt;(null) - 这正是我在任务并行库中错过的内容
【解决方案2】:

这样做的一种方法是,如果任务为空,则在循环中创建任务,但您提供的代码对我来说看起来更好:

Task task = null;
foreach (var msg in messages)
{   
    if (task == null)
      task = new Task(() => Console.WriteLine(msg))
    else
      task = task.ContinueWith(t => Console.WriteLine(msg));
}
task.Start();

【讨论】:

  • 技术上 - 是的,但这意味着我必须在每个循环中检查它,这样它会更慢。性能很重要,我应该将此添加到问题中
  • 您提供的代码非常好,但让我们等待其他选项
【解决方案3】:

也许是这样的:

if(messages.Length > 0)
{
    Task task = new Task(t => Console.WriteLine(messages[0]));

    for(int i = 1; i < messages.Length; i++)
    {
        task = task.ContinueWith(t => Console.WriteLine(messages[i]));
    }
    task.Start();
}

【讨论】:

  • 谢谢,这是一个选项,但并不总是有效。有时你只有枚举器,也可以通过其他方式添加任务
  • @Anri 你也可以用枚举器做同样的事情(通过手动访问它的MoveNext()Current),除了它会更多的代码甚至更混乱。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-25
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 2019-11-14
  • 2019-04-24
  • 1970-01-01
相关资源
最近更新 更多