链接任务的正确方法

时间:2012-11-26 16:34:16

标签: c# multithreading task-parallel-library

我想链接Task,然后并行启动链。 这个片段只是为了说明我的问题:

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

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

有什么办法可以避免吗?

我确实理解它几乎不会影响性能(除非你经常这样做),但仍然如此。在我的情况下,性能很重要,因此检查每次迭代中是否存在任务都不是实现它的方法。

3 个答案:

答案 0 :(得分:3)

你可以这样做:

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));

答案 1 :(得分:2)

这样做的一种方法是在循环中创建任务,如果它是null,但是你提供的代码看起来更适合我:

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();

答案 2 :(得分:1)

也许这就是:

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();
}