#c_sharp #net #async_await
Есть список задач, каждую задачу нужно ограничить по времени. Если задача не успевает выполниться, то пусть возвращается какое-то дефолтное значение. class Program { static void Main(string[] args) { var sw = new Stopwatch(); sw.Start(); foreach (var i in GetInts()) { Console.WriteLine("result: " + i); } sw.Stop(); Console.WriteLine("done " + sw.Elapsed.TotalMilliseconds.ToString("F2")); Console.ReadKey(); } static ListGetInts() { using (var cts = new CancellationTokenSource()) { var tasks = new Task [3]; tasks[1] = Task.Run(() => { int i = 5000; Thread.Sleep(i); Console.WriteLine(i); return i; }, cts.Token); tasks[0] = Task.Run(() => { int i = 3000; Thread.Sleep(i); Console.WriteLine(i); return i; }, cts.Token); tasks[2] = Task.Run(() => { int i = 2000; Thread.Sleep(i); Console.WriteLine(i); return i; }, cts.Token); var delay = 1000; return tasks.Select(x => x.TimeoutAfter(delay, cts)).Select(x => x.Result).ToList(); } } } public static class TaskExtensions { public static async Task TimeoutAfter (this Task task, int delay, CancellationTokenSource cts) { using (var timeoutCancellationTokenSource = new CancellationTokenSource()) { var completedTask = await Task.WhenAny(task, Task.Delay(delay, timeoutCancellationTokenSource.Token)); if (completedTask == task) { timeoutCancellationTokenSource.Cancel(); return await task; } else { cts.Cancel(); return default(TResult); } } } } Хотя в коде прописано ограничение в 1000мс, данный код выводит следующий результат: 2000 result: 0 result: 0 result: 2000 done 2010,83 3000 5000 Почему задача с 2с попадает в result? А если выставить delay = 3000, то выведутся все задачи (хотя 5с не должна): 2000 3000 5000 result: 3000 result: 5000 result: 2000 done 5008,06
Ответы
Ответ 1
Смотрите. Вы не можете завершить бегущую задачу насильно, точно так же как вы не можете завершить насильно любой другой код. Для этого задача должна сотрудничать. Идея с использованием CancellationToken'а правильная, но не доведена до конца, отсюда и проблема. CancellationToken в Task.Run относится только к процессу запуска таска*. Когда таск запущен, он не сможет магическим образом узнать о том, что токен сработал, ему нужно самому проверять отмену токена. Например, так: var ct = cts.Token; tasks[0] = Task.Run(async () => // добавил async { int i = 3000; await Task.Delay(i, ct); // проверяем токен Console.WriteLine(i); return i; }, ct); Ещё по теме: Как работает CancellationToken в TaskFactory.StartNew Method (Action, CancellationToken)? *Почти. Ещё он ассоциируется с таском, и при выбросе исключения этим токеном таск переходит в состояние Cancelled, а не Faulted.
Комментариев нет:
Отправить комментарий