6

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

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

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

有什么办法可以避免吗?

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

4

3 回答 3

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));
于 2012-11-26T17:11:31.693 回答
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();
于 2012-11-26T16:36:37.140 回答
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();
}
于 2012-11-26T16:47:54.810 回答