6

I want to chain Tasks, then start the chain in parallel. This snippet is just to illustrate my question:

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

Everything works fine except a little perfectionist inside me doesn't like having empty method executed first () => { }.

Is there any way to avoid it?

I do understand It's barely affecting performance (unless you do it really often), but still. Performance matters in my case, so checking if task exists in every iteration is not the way to do it.

3
  • 1
    "Performance matters in my case, so checking if task exists in every iteration is not the way to do it.": the time it takes is negligible compared to actually executing the task. Unless you have really measured a performance hit, it's clearly a case of premature optimization. Commented Nov 26, 2012 at 16:55
  • @ThomasLevesque you are probably right, i just thought maybe i missed something in Task creating API. I will have a chance to measure performance hit later. Commented Nov 26, 2012 at 17:00
  • You might find TPL DataFlow interesting Commented Nov 27, 2012 at 13:31

3 Answers 3

3

You could do this:

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

The previous solution won't work in 4.0. In 4.0 you'd need to do the following instead:

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

(You can move SetResult to before the foreach loop if you prefer.)

Technically it's not the same as the continuations will start executing while you're still adding more. That's unlikely to be a problem though.

Another option would be to use something like this:

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

An example usage would be:

ForEachAsync(messages, msg => Console.WriteLine(msg)); 
Sign up to request clarification or add additional context in comments.

3 Comments

One note: Task.FromResult() is new in .Net 4.5, in .Net 4.0, you would need to do this manually using TaskCompletionSource.
@svick Right, solution added.
Nice, thanx, Task.FromResult<object>(null) - this is exactly what i missed in the Task Parallel lib
2

One way to do that, is to create task in loop if it is null, but the code you provide looks better for me:

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 Comments

Technically - yes, but it means i will have to check it every loop so it will be even slower. Performance matters, I should add this to question
code you provide is really nice, but let's wait for other options
1

Perhaps this:

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

2 Comments

Thanks, it's an option, but will not always work. sometimes all you have is enumerator, and tasks also can be added in other ways
@Anri You can do the same thing with enumerator too (by manually accessing its MoveNext() and Current), except it will be even more code and even more messy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.