21

I'm using the .NET 4.0 Task Parallel Library with C# (my first time using TPL)

I have a task A which I want to run to completion before firing off a bunch of other tasks (B,C,D, etc). I therefore want to create tasks B,C,D etc as continuations of task A. However, I want to pass a 'state' object to task B, another state object to task C, etc.

I can pass a state object to task A by simply using a Task constructor overload that takes a state object, for example http://msdn.microsoft.com/en-us/library/dd783035.aspx describes this Task constructor overload:

Task(Action<Object>, Object, CancellationToken) 

This works fine, and the second argument is my 'state' object.

I want to create a continuation task, e.g. for task B:

Task taskB = taskA.ContinueWith(/* args here*/)

However, I cannot see a ContinueWith() overload (see http://msdn.microsoft.com/en-us/library/dd235663.aspx) which allows me to pass a 'state' object to a continuation task. How can this be done?

Notes:

  1. I do not have the 'state' object for taskB available at the time I create taskA
  2. The 'state' object for taskB is not an output (return value) of taskA

For some context, what I am doing is creating taskB, taskC, etc. inside a couple of loops and so I am passing the value of the loop variables to taskB, taskC, etc. using a state object, in order to avoid the problem of always ending up with the final value of the loop variables in the tasks (the closure issue).

sashoalm
  • 75,001
  • 122
  • 434
  • 781
Hugh Robinson
  • 333
  • 1
  • 5
  • 10

3 Answers3

25

The simplest approach would probably be to simply capture it in the Func<Task, TResult> you pass into ContinueWith. For example:

object taskBState = GetStateHere();
Task taskB = taskA.ContinueWith(task => RealContinuation(task, taskBState));

Personally I find it easier to capture state like that than getting the state passed in anyway.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks for the quick reply. I tried what you suggest in a test program and it worked fine. I know I need to improve my understanding of closures... and yes, this does feel like an easier way of capturing state... – Hugh Robinson May 13 '11 at 14:41
  • 4
    Unfortunately MS changed their mind about this ([link](http://blogs.msdn.com/b/pfxteam/archive/2011/11/10/10235962.aspx)), after realizing some of the performance impact vs. state parameters in long continuation chains with closures. You can write code that works in both 4.0 and 4.5, but wont take advantage of significant performance improvement in 4.5; or write 4.5-specific code with an explicit parameter and no closure. – danwyand Jun 03 '13 at 19:17
3

For future readers, ContinueWith() function, at least in .NET 4.7, has an overload that allows you to pass state parameter:

public System.Threading.Tasks.Task ContinueWith(
  Action<System.Threading.Tasks.Task,object> continuationAction, object state,
  System.Threading.CancellationToken cancellationToken, 
  System.Threading.Tasks.TaskContinuationOptions continuationOptions, 
  System.Threading.Tasks.TaskScheduler scheduler);

See this MSDN article for more details.

dotNET
  • 33,414
  • 24
  • 162
  • 251
3

You can't. They expect you to use the power of closures. Just define an additional variable inside the loops to capture the current value for that closure. See this answer from Jon Skeet for more details on capturing and closures.

Update: Or Jon Skeet could beat me to reply directly to your question saying exactly the same thing. :)

Community
  • 1
  • 1
Drew Marsh
  • 33,111
  • 3
  • 82
  • 100