2012-11-26 36 views
5

我想连锁Task s,然后并行启动链。 这个片段只是为了说明我的问题:适当的方式来连锁任务

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

一切正常,只是有点完美主义里我不喜欢有空方法执行第一() => { }

有什么办法避免呢?

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

+1

“在我的情况下的性能问题,所以检查是否存在任务在每次迭代中不做到这一点的方式“:与实际执行任务相比,所需时间可以忽略不计。除非您确实衡量了性能影响,否则显然是过早优化的情况。 –

+0

@ThomasLevesque你可能是对的,我只是想,也许我错过了'任务'创建API中的东西。我将有机会衡量后来的表现。 – Anri

+0

你可能会发现TPL DataFlow很有趣 – sll

回答

3

你可以这样做:

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

以前的解决方案将无法在4.0中工作。在4.0你需要做的,而不是以下几点:(您可以在foreach循环之前,如果你喜欢移动SetResult到)

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

tcs.SetResult(null); 

从技术上讲这是不一样的延续将开始执行,而你还在增加更多。但这不太可能是个问题。

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

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

有一点需要注意:在.NET 4.0中,'Task.FromResult()'是新的.Net 4.0中,您需要使用TaskCompletionSource手动执行此操作。 – svick

+0

@svick正确的解决方案。 – Servy

+0

不错,thanx,'Task.FromResult (null)' - 这正是我在Task Parallel lib中错过的 – Anri

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

技术上 - 是的,但这意味着我将不得不在每一个循环中检查它,所以它会变得更慢。性能很重要,我应该加上这个问题 – Anri

+0

你提供的代码是非常好的,但我们等待其他选项 –

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

这是最有效的选项,但它的可读性较差... –

+0

谢谢,这是一个选项,但不会总是工作。有时你拥有的只是枚举器,任务也可以通过其他方式添加 – Anri

+0

@Anri你也可以用枚举器做同样的事情(通过手动访问它的'MoveNext()'和'Current'),除了它会更多代码甚至更混乱。 – svick