Why is the TaskFactory.StartNew method not generic?
Asked Answered
H

2

9

The idomatic way to start a new side-effect-only task (that is: a task that returns no result) using the TPL in .NET 4.0 is using the following API:

Task Task.Factory.StartNew(Action<object>, object)   

But why doesn't the signature of this API look like this

Task Task.Factory.StartNew<T>(Action<T>, T) 

or like this

Task Task.Factory.StartNew<T>(T, Action<T>) 

Technical reasons or some other reason?

Howler answered 3/12, 2009 at 14:10 Comment(0)
E
7

Okay, now that I understand the question properly :)

I believe it's because this is meant to be a direct replacement for ThreadPool.QueueUserWorkItem. I do agree that it seems somewhat odd... but if you're using lambda expressions anyway, it's probably easier to use the version that does take a state parameter (i.e. Action instead of Action<object>) and just capture the value you're interested in beforehand. It doesn't help if you're specifying the value and the function separately :(

Elias answered 3/12, 2009 at 14:13 Comment(3)
Yes, but Task<T> is generic in the result type (TResult) of a task, but not generic in the type of the "initial state" (that is: the input for a task).Howler
So... when I call Task.Factory.StartNew does it grab a thread from the threadpool automagically?Picket
@Padu: Yes, I believe so - although you can have your own task factory which uses a different set of threads, I believe.Elias
C
4

According to a post by Stephen Toub (MSFT), they are assuming that we are going to rely on closures to pass state data. There was also some excuse about signature ambiguity. (http://social.msdn.microsoft.com/Forums/en/parallelextensions/thread/1988294c-de41-476a-a104-aa550b7409f5)

However, relying on closures to solve this problem seems like a temporary hack waiting for a better solution. It works, but it's not a good longer-term solution. There are many times that simply specifying a delegate method as the action would be the simplest approach, but that means we have to use global vars or we're excluded from state parameter passing.

I like one of Hugo's proposals (from the MS forum posting). Hugo suggested introducing a TaskState type, which seems like a clever way to circumvent the generics ambiguity issue.

Applying this to the Task.Factory.StartNew() signature and the Task() constructor as such:

  public Task<T>( Action<T> function, TaskState<T> state );
  public Task<T,TResult>( Func<T,TResult> function, TaskState<T> state );

ActionState would be a lot like the Nullable class -- just a simple wrapper around a Value member. In practice, using TaskState might look like this:

  var myTask = new Task( MyMethod, new TaskState( stateInfo ) );
  ...

  public void MyMethod( StateInfo stateInfo ) { ... }

The TaskState<> solution is not perfect, but it seems like a much better solution than relying on closure of type casting.

Contra answered 11/3, 2011 at 21:44 Comment(1)
Funny, the TPL team just released a white paper about the improvements in 4.5; in the "best practices" section (p. 11), they describe that it is best to pass in the state explictly (via the Action<object> overload) instead of capturing variables in the closure. Now, with regard to this recommendation, a generic overload would be certainly desireable.Howler

© 2022 - 2024 — McMap. All rights reserved.