Почему метод TaskFactory.StartNew не является универсальным? - PullRequest
9 голосов
/ 03 декабря 2009

Идоматический способ запуска новой задачи, связанной только с побочными эффектами (то есть: задача, которая не возвращает результата) с использованием TPL в .NET 4.0, использует следующий API:

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

Но почему подпись этого API не выглядит так

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

или как это

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

Технические причины или другая причина?

Ответы [ 2 ]

7 голосов
/ 03 декабря 2009

Хорошо, теперь, когда я правильно понял вопрос:)

Я полагаю, это потому, что это прямая замена на ThreadPool.QueueUserWorkItem. Я согласен, что это выглядит несколько странно ... но если вы все равно используете лямбда-выражения, вероятно, проще использовать версию, в которой действительно принимает параметр состояния (то есть Action вместо Action<object>) и просто запишите интересующее вас значение заранее. Не поможет, если вы указываете значение и функцию отдельно: (

3 голосов
/ 12 марта 2011

Согласно сообщению Стивена Тауба (MSFT), они предполагают, что мы будем полагаться на замыкания для передачи данных о состоянии. Был также некоторый оправдание неоднозначности подписи. (http://social.msdn.microsoft.com/Forums/en/parallelextensions/thread/1988294c-de41-476a-a104-aa550b7409f5)

Однако полагаться на замыкания для решения этой проблемы кажется временным взломом, ожидающим лучшего решения. Это работает, но это не хорошее долгосрочное решение. Во многих случаях простое указание метода делегата в качестве действия было бы самым простым подходом, но это означает, что мы должны использовать глобальные переменные или мы исключены из передачи параметров состояния.

Мне нравится одно из предложений Хьюго (из сообщений на форуме MS). Хьюго предложил ввести тип TaskState, который кажется разумным способом обойти проблему неоднозначности обобщений.

Применение этого к подписи Task.Factory.StartNew () и конструктору Task () следующим образом:

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

ActionState будет во многом похож на класс Nullable - просто простая оболочка для члена Value. На практике использование TaskState может выглядеть так:

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

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

Решение TaskState <> не идеально, но кажется, что оно гораздо лучше, чем полагаться на закрытие приведения типов.

...