Встраивание задачи и Task.Wait

#c# #.net #task-parallel-library

#c# #.net #задача-параллельная-библиотека

Вопрос:

Я только что понял, что когда я запускаю задачу изнутри задачи и вызываю 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.Resu<
   cts.Cancel();
   throw new TimeoutException("");
}
  

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

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

Комментарии:

1. Ваш текущий код никогда не запускает задачу. Либо вызовите ‘task.Start’, либо, что еще лучше, используйте Task.Factory.StartNew . Смотрите blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx

Ответ №1:

Уверен, что это невозможно. Предположим, вы выполняете следующий код в потоке 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 amp;amp; !cancellationToken.CanBeCanceled amp;amp;
       WrappedTryRunInline() amp;amp; IsCompleted) 
   {
       returnValue = true; 
   } 
   else
   { 
       returnValue = CompletedEvent.Wait(millisecondsTimeout, cancellationToken);
   }
  

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

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

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

Комментарии:

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

2. Ну, у меня был один ответ здесь, который объяснял, почему задача не может быть встроена, и я построил решение вокруг этого. Но ответ был удален до того, как я смог его принять. Итак, вопрос был забыт….

3. Ну, я не получал значок некроманта 9 раз без причины 😉