Использование CancellationToken для таймаута в задаче.Run не работает
хорошо, мои вопросы очень просты. Почему этот код не выдает TaskCancelledException
?
static void Main()
{
var v = Task.Run(() =>
{
Thread.Sleep(1000);
return 10;
}, new CancellationTokenSource(500).Token).Result;
Console.WriteLine(v); // this outputs 10 - instead of throwing error.
Console.Read();
}
но не работает
static void Main()
{
var v = Task.Run(() =>
{
Thread.Sleep(1000);
return 10;
}, new CancellationToken(true).Token).Result;
Console.WriteLine(v); // this one throws
Console.Read();
}
4 ответов
отмена является кооперативной и не навязывается слушателю. Прослушиватель определяет, как изящно завершить работу в ответ на запрос отмены.
вы не написали никакого кода внутри вашего Task.Run
метод доступа к вашему CancellationToken
и реализовать отмену-таким образом, вы фактически проигнорировали запрос на отмену и побежали к завершению.
Я думаю, потому что вы не вызов метода ThrowIfCancellationRequested()
из объекта CancellationToken.
Таким образом, вы игнорируете просьбу об отмене задания.
вы должны сделать что-то вроде этого:
void Main()
{
var ct = new CancellationTokenSource(500).Token;
var v =
Task.Run(() =>
{
Thread.Sleep(1000);
ct.ThrowIfCancellationRequested();
return 10;
}, ct).Result;
Console.WriteLine(v); //now a TaskCanceledException is thrown.
Console.Read();
}
второй вариант вашего кода работает, потому что вы уже инициализируете токен с помощью Canceled
состояние установлено в true. Действительно, как сказано здесь:
If canceled is true, both CanBeCanceled and IsCancellationRequested will be true
отмена уже запрошено, а затем исключение TaskCanceledException
будет немедленно брошен, фактически не начиная задачу.
есть разница в отмене запущенной задачи, и задания выполнить.
после вызова задачи.Запустите метод, задача только запланирована и, вероятно, еще не выполнена.
при использовании задачи.Работать.(.., CancellationToken) семейство перегрузок с поддержкой отмены маркер отмены проверяется, когда задача собирается запустить. Если маркер отмены имеет iscancellationrequested значение true в это время, исключение типа Создается исключение TaskCanceledException.
если задача уже запущена, задача должна вызвать метод ThrowIfCancellationRequested или просто вызвать исключение OperationCanceledException.
согласно MSDN, это просто удобный метод для следующего:
if (токен.IsCancellationRequested) выбросить новое исключение OperationCanceledException (токен);
не другой вид исключения, используемого в этих двух случаи:
catch (TaskCanceledException ex)
{
// Task was canceled before running.
}
catch (OperationCanceledException ex)
{
// Task was canceled while running.
}
также обратите внимание, что TaskCanceledException
происходит от OperationCanceledException
, Так что вы можете иметь один catch
статьи OperationCanceledException
тип:
catch (OperationCanceledException ex)
{
if (ex is TaskCanceledException)
// Task was canceled before running.
// Task was canceled while running.
}
другая реализация с использованием задачи.Задержка с token вместо этого поток.Спать.
static void Main(string[] args)
{
var task = GetValueWithTimeout(1000);
Console.WriteLine(task.Result);
Console.ReadLine();
}
static async Task<int> GetValueWithTimeout(int milliseconds)
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
cts.CancelAfter(milliseconds);
token.ThrowIfCancellationRequested();
var workerTask = Task.Run(async () =>
{
await Task.Delay(3500, token);
return 10;
}, token);
try
{
return await workerTask;
}
catch (OperationCanceledException )
{
return 0;
}
}