Задача встраиваемая и Task.Wait - PullRequest
1 голос
/ 07 ноября 2011

Я только что понял, что когда я запускаю задачу из задачи и вызываю Task.Wait, новая задача не будет встроена, а вызов Task.Result всегда будет встроен в задачу.

Поскольку мы обертываем наши задачи шаблоном RAII (реализовано в ExecuteWithCancel), встраивание будет повторно использовать выделенные ресурсы и является предпочтительным.

Но мы иногда хотим подождать некоторое время и отменить задание после этого. Код ожидания выглядит следующим образом:

using (var cts = new CancellationTokenSource())
{
   // Task scheduler decides whether to execute synchronous or asynchronous
   var task = new Task<TResult>(() => ExecuteWithCancel<TResult>(cts.Token, nameOfTaskPerformer, arguments), cts.Token)
   if (timeout==TimeSpan.Zero || task.Wait(timeout)) // this creates an all or nothing timeout
      return task.Result;
   cts.Cancel();
   throw new TimeoutException("");
}

Когда тайм-аут TimeSpan.Zero, задача встроена, в противном случае она всегда использует другой поток.

Есть ли простой способ перепроектировать этот код для использования встраивания и ожидания / тайм-аута?

1 Ответ

3 голосов
/ 24 марта 2012

Уверен, что это невозможно. Предположим, вы выполняете следующий код в потоке A:

var task = Task.Factory.StartNew(() => Thread.Sleep(Timeout.Infinite));
task.Wait(5000);

Если задача встроена, поток A будет блокироваться на неопределенный срок - как он проснется после истечения времени ожидания? Глядя на справочный источник (Task.cs), мы можем видеть именно это:

internal bool InternalWait(int millisecondsTimeout, CancellationToken cancellationToken) 
{
   ...
   // we will attempt inline execution only if an infinite wait was requested 
   // Inline execution doesn't make sense for finite timeouts and if a cancellation token was specified
   // because we don't know how long the task delegate will take. 
   if (millisecondsTimeout == Timeout.Infinite && !cancellationToken.CanBeCanceled &&
       WrappedTryRunInline() && IsCompleted) 
   {
       returnValue = true; 
   } 
   else
   { 
       returnValue = CompletedEvent.Wait(millisecondsTimeout, cancellationToken);
   }

В соответствии с вашим вопросом, чтобы извлечь выгоду из встраивания с конечными тайм-аутами, вам нужно реализовать логику тайм-аута внутри самой задачи, возможно, что-то вроде:

ExecuteWithCancel<TResult>(cts.Token, TimeSpan timeout, nameOfTaskPerformer, arguments)

А затем используйте обычный Wait() (или Result).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...