0

I'm looking at the last example from page http://msdn.microsoft.com/en-us/library/dd997415(v=vs.110).aspx

var task1 = Task.Factory
       .StartNew(() => {...})
       .ContinueWith((t) => {...})

The idea of example that task is placed to thread pool and some post-process handler registered for just created task. But this code looks dangerous even with respect to .Net atomicity of operations.

Instead I'd propose:

var task1 = new Task(()=>{...})
task1.ContinueWith((t) => {...})
task1.Start();

So my doubt about first form that exists a chance of task accomplishment before it is assigned with post-process handler. Please provide some feedback and explanation if I'm wrong.

Dewfy
  • 23,277
  • 13
  • 73
  • 121

2 Answers2

2

The fluent syntax is a little easier to read imo, though that's just a matter of style.

More to your point though, the continuation task is blocked from starting until the antecedent has completed, rather than being triggered specifically by the completion of the antecedent. This means that if the previous task happened to have completed before ContinueWith() returned, the continuation task would just start immediately (since it would not be blocked from starting), rather than not being triggered.

This isn't very clearly documented, though this is a useful article on continuations: http://msdn.microsoft.com/en-us/library/ee372288%28v=vs.110%29.aspx

See also first answer here: http://social.msdn.microsoft.com/Forums/vstudio/en-US/03f14ead-8b68-47bd-83fa-be9d2f6014b2/how-to-use-continuewith?forum=parallelextensions

"If ContinueWith is called on an already completed task, the continuation will still be run correctly, being scheduled immediately for execution. As you intimate, if we didn't do that, there would be a serious race condition that would make using ContinueWith very error prone"

Jon G
  • 4,083
  • 22
  • 27
  • One of my tags is about 'multithreading' - so `TaskContinuationOptions.ExecuteSynchronously` is bad example when you can assign continuation task. Also fluent syntax of c# has no special language constructions to notify class that building was done - so I think you didn't provide correct answer – Dewfy Feb 13 '14 at 00:21
  • What I'm saying is that you could start the first task, let it run to completion, then call ContinueWith on it with a new task and the second task would start immediately, rather than what I think is your concern, being that the second task wouldn't be triggered because the first had already completed. – Jon G Feb 13 '14 at 00:23
  • You can do it only for `ExecuteSynchronously` - that is not compatible with multithreading. – Dewfy Feb 13 '14 at 00:25
  • No, this is the behaviour regardless of TaskContinuationOptions, i.e. the continuation task is blocked until the antededent completes. If the antecedent is already complete when the continuation is created then it will just start immediately – Jon G Feb 13 '14 at 00:28
  • Let's stop this non-relevant discussion because I'm not going use flag ExecuteSynchronously - that violates idea of parallel processing. – Dewfy Feb 13 '14 at 00:31
  • Okay, have edited my answer to eliminate this confusion. ExecuteSynchronously is irrelevant! – Jon G Feb 13 '14 at 00:32
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/47369/discussion-between-jon-g-and-dewfy) – Jon G Feb 13 '14 at 00:33
1

There is no race condition in the first code fragment, unless you access task1 from inside the task's lambda. Even if the task completes before ContinueWith is attached to it, the ContinueWith lambda will be called anyway.

You don't need to access task1 inside the ContinueWith lambda, you're provided with antecedentTask argument for that:

var task1 = Task.Factory.StartNew(() => {/* ... */}).ContinueWith(
    (antecedentTask) => { /* use antecedentTask here, not task1 */ }); 

Task.Factory.StartNew is a recommended way of starting a task over new Task(), and Task.Run is recommended over Task.Factory.StartNew. Check these blog posts by Stephen Toub:

"Task.Factory.StartNew" vs "new Task(...).Start"

and

"Task.Run vs Task.Factory.StartNew"

In my experience, you may only need to construct a task via new Task() constructor if you need to access the Task object from the task's own action, like this, which should be very rare.

Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • my apologies if you understand my question as doubt about race condition. **Jon G** above reworded my question as *"... task might not be triggered because the first had already completed"*. But after the hint of Jon and analyzing Task sources I see that event when task completed continuation is invoked. – Dewfy Feb 13 '14 at 08:31
  • @Dewfy, then I guess I didn't get right what you're asking, and now I'm afraid I still don't get. Which means I mistakenly up-voted it. – noseratio Feb 13 '14 at 08:42
  • Actually if you go to mentioned article "Task.Factory.StartNew" vs "new Task(...).Start" you can see exactly my topic headline *There is a chance that the ThreadPool will pick up the scheduled task and execute it before the Task reference returned from StartNew is stored into t.* – Dewfy Feb 13 '14 at 09:29
  • @Dewfy, that's exactly what I pointed out in my answer: *you may only need to construct a task via new Task() constructor if you need to access the Task object from the task's own action*. This concern applies to the lambda passed to `Task.Factory.StartNew`, but does *not* apply to the lambda passed to `ContinueWith`, as in your question. – noseratio Feb 13 '14 at 10:15