#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 раз без причины 😉