Task.Delay Выдаёт Ошибку при Ожидании Дольше 49,7 Дней
В последний рабочий день года немного (бес)полезной информации.
Метод Task.Delay завершается с ошибкой, если вы ждёте дольше 49,7 дней. Т.е.
await Task.Delay(TimeSpan.FromDays(50));
завершится ошибкой.
System.ArgumentOutOfRangeException: The value needs to translate in milliseconds to -1 (signifying an infinite timeout), 0, or a positive integer less than or equal to the maximum allowed timer duration. (Значение должно в миллисекундах соответствовать -1 (что означает бесконечный таймаут), 0 или положительному целому числу, меньшему или равному максимально допустимой длительности таймера.)
При этом TimeSpan.FromDays(49) работает. Почему так происходит, мы же используем положительное число. И стоит ли об этом беспокоиться?
«Под капотом» Task.Delay в качестве базовой единицы измерения использует uint и выполняет такую проверку:
internal static uint
ValidateTimeout(
TimeSpan timeout,
ExceptionArgument argument)
{
long totalMilliseconds = (long)timeout.TotalMilliseconds;
if (totalMilliseconds < -1L
|| totalMilliseconds > 4294967294L)
ThrowHelper.ThrowArgumentOutOfRangeException(
argument,
ExceptionResource.Task_InvalidTimerTimeSpan);
return (uint)totalMilliseconds;
}
Поэтому максимальное значение равно uint.MaxValue-1, что равно "4294967294U". Это в миллисекундах, либо: 4294967294мс / 1000 (секунд) / 60 (минут) / 60 (часов) / 24 (дней) = 49,7103 дня.
Стоит ли об этом беспокоиться?
Скорее всего, нет. Только если вы не пишете какую-нибудь фоновую логику, которая должна отложить выполнение работы на очень далёкое будущее. Например, свой планировщик заданий, где пользователь может ввести любую нотацию CRON, и это должно работать. Так что, если пользователь хочет, чтобы задание выполнялось, скажем, только раз в год, конечно, это должно быть доступно. Что делать? Можно вызывать Task.Delay в цикле, пока всё время не истечёт:
internal static class TaskExtensions
{
public static async Task LongDelaySafe(
TimeSpan targetTimeSpan,
TimeProvider timeProvider,
CancellationToken token)
{
ArgumentOutOfRangeException
.ThrowIfLessThanOrEqual(
targetTimeSpan,
TimeSpan.Zero);
var remaining = targetTimeSpan;
while (remaining > TimeSpan.Zero)
{
var delay =
TimeSpan.FromMilliseconds(
Math.Min(
remaining.TotalMilliseconds,
uint.MaxValue-1)
);
await Task.Delay(delay, timeProvider, ct)
.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
remaining -= delay;
}
}
}
Источник: https://steven-giesel.com/blogPost/c3bdad0d-f9eb-4dcf-b650-98ec0f5dd4d4/taskdelay-fails-if-you-wait-longer-than-497-days