Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the TaskFactory.StartNew method not generic?

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?

like image 731
Frank Avatar asked Dec 03 '09 14:12

Frank


2 Answers

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 :(

like image 65
Jon Skeet Avatar answered Nov 18 '22 09:11

Jon Skeet


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.

like image 3
Jonathan B. Avatar answered Nov 18 '22 07:11

Jonathan B.