Task.Delay 超过 int.MaxValue 毫秒
Task.Delay for more than int.MaxValue milliseconds
可以告诉 Task.Delay
延迟的最大持续时间是 int.MaxValue
毫秒。创建一个将延迟超过该时间的 Task
的最干净的方法是什么?
// Fine.
await Task.Delay(TimeSpan.FromMilliseconds(int.MaxValue));
// ArgumentOutOfRangeException
await Task.Delay(TimeSpan.FromMilliseconds(int.MaxValue + 1L));
你不能。如果您传递的值解析为比 Int32.MaxValue 更大的毫秒数,则每次重载都会引发 ArgumentOutOfRange
异常。即使对于 TimeSpan
重载 (MSDN).
也是如此
您最多只能等待两次:
await Task.Delay(TimeSpan.FromMilliseconds(int.MaxValue));
await Task.Delay(TimeSpan.FromMilliseconds(20));
您可以轻松编写一个方法将其分解为更小的延迟:
private static readonly TimeSpan FullDelay = TimeSpan.FromMilliseconds(int.MaxValue);
private static async Task LongDelay(TimeSpan delay)
{
long fullDelays = delay.Ticks / FullDelay.Ticks;
TimeSpan remaining = delay;
for(int i = 0; i < fullDelays; i++)
{
await Task.Delay(FullDelay);
remaining -= FullDelay;
}
await Task.Delay(remaining);
}
您可以延迟多次。例如:
static async Task LongDelay(long milliseconds)
{
if (milliseconds < 0)
{
throw new ArgumentOutOfRangeException();
}
if (milliseconds == 0)
{
return;
}
int iterations = (milliseconds - 1) / int.MaxValue;
while (iterations-- > 0)
{
await Task.Delay(int.MaxValue);
milliseconds -= int.MaxValue;
}
await Task.Delay(milliseconds);
}
也就是说,int.MaxValue
毫秒是真的 很长的时间,将近 25 天!恕我直言,一个更重要的问题是,Task.Delay()
方法真的是您场景的最佳解决方案吗?详细了解您为什么要等待这么长时间可能会帮助其他人为您提供更好的解决实际问题的方法,而不是解决这个非常具体的需求。
您无法使用单个 Task.Delay
实现这一点,因为它在内部使用仅接受 int 的 System.Threading.Timer
。
但是,您可以一个接一个地使用多个等待来做到这一点。这是最干净的方法:
static async Task Delay(long delay)
{
while (delay > 0)
{
var currentDelay = delay > int.MaxValue ? int.MaxValue : (int) delay;
await Task.Delay(currentDelay);
delay -= currentDelay;
}
}
如果您关心精度,您应该使用 Stopwatch
而不是将 delay
分成 Int16.MaxValue
个块。这就是以下代码与其他答案的不同之处:
private static async Task LongDelay(TimeSpan delay)
{
var st = new System.Diagnostics.Stopwatch();
st.Start();
while (true)
{
var remaining = (delay - st.Elapsed).TotalMilliseconds;
if (remaining <= 0)
break;
if (remaining > Int16.MaxValue)
remaining = Int16.MaxValue;
await Task.Delay(TimeSpan.FromMilliseconds(remaining));
}
}
更新:根据@CoryNelson 的评论,Stopwatch
对于长圈来说不够好。如果是这样,可以简单地使用 DateTime.UtcNow
:
private static async Task LongDelay(TimeSpan delay)
{
var start = DateTime.UtcNow;
while (true)
{
var remaining = (delay - (DateTime.UtcNow - start)).TotalMilliseconds;
if (remaining <= 0)
break;
if (remaining > Int16.MaxValue)
remaining = Int16.MaxValue;
await Task.Delay(TimeSpan.FromMilliseconds(remaining));
}
}
从 .NET 6 开始,Task.Delay
method is now 4,294,967,294 milliseconds (0xFFFFFFFE
, or UInt32.MaxValue - 1
), which is approximately 49 days and 17 hours. It is the same limit with the dueTime
/period
arguments of the System.Threading.Timer
构造函数的最大有效 TimeSpan delay
值,Task.Delay
在内部基于该值。
相关 GitHub 问题:Task.Delay
actual accepted delay is twice the documented value
可以告诉 Task.Delay
延迟的最大持续时间是 int.MaxValue
毫秒。创建一个将延迟超过该时间的 Task
的最干净的方法是什么?
// Fine.
await Task.Delay(TimeSpan.FromMilliseconds(int.MaxValue));
// ArgumentOutOfRangeException
await Task.Delay(TimeSpan.FromMilliseconds(int.MaxValue + 1L));
你不能。如果您传递的值解析为比 Int32.MaxValue 更大的毫秒数,则每次重载都会引发 ArgumentOutOfRange
异常。即使对于 TimeSpan
重载 (MSDN).
您最多只能等待两次:
await Task.Delay(TimeSpan.FromMilliseconds(int.MaxValue));
await Task.Delay(TimeSpan.FromMilliseconds(20));
您可以轻松编写一个方法将其分解为更小的延迟:
private static readonly TimeSpan FullDelay = TimeSpan.FromMilliseconds(int.MaxValue);
private static async Task LongDelay(TimeSpan delay)
{
long fullDelays = delay.Ticks / FullDelay.Ticks;
TimeSpan remaining = delay;
for(int i = 0; i < fullDelays; i++)
{
await Task.Delay(FullDelay);
remaining -= FullDelay;
}
await Task.Delay(remaining);
}
您可以延迟多次。例如:
static async Task LongDelay(long milliseconds)
{
if (milliseconds < 0)
{
throw new ArgumentOutOfRangeException();
}
if (milliseconds == 0)
{
return;
}
int iterations = (milliseconds - 1) / int.MaxValue;
while (iterations-- > 0)
{
await Task.Delay(int.MaxValue);
milliseconds -= int.MaxValue;
}
await Task.Delay(milliseconds);
}
也就是说,int.MaxValue
毫秒是真的 很长的时间,将近 25 天!恕我直言,一个更重要的问题是,Task.Delay()
方法真的是您场景的最佳解决方案吗?详细了解您为什么要等待这么长时间可能会帮助其他人为您提供更好的解决实际问题的方法,而不是解决这个非常具体的需求。
您无法使用单个 Task.Delay
实现这一点,因为它在内部使用仅接受 int 的 System.Threading.Timer
。
但是,您可以一个接一个地使用多个等待来做到这一点。这是最干净的方法:
static async Task Delay(long delay)
{
while (delay > 0)
{
var currentDelay = delay > int.MaxValue ? int.MaxValue : (int) delay;
await Task.Delay(currentDelay);
delay -= currentDelay;
}
}
如果您关心精度,您应该使用 Stopwatch
而不是将 delay
分成 Int16.MaxValue
个块。这就是以下代码与其他答案的不同之处:
private static async Task LongDelay(TimeSpan delay)
{
var st = new System.Diagnostics.Stopwatch();
st.Start();
while (true)
{
var remaining = (delay - st.Elapsed).TotalMilliseconds;
if (remaining <= 0)
break;
if (remaining > Int16.MaxValue)
remaining = Int16.MaxValue;
await Task.Delay(TimeSpan.FromMilliseconds(remaining));
}
}
更新:根据@CoryNelson 的评论,Stopwatch
对于长圈来说不够好。如果是这样,可以简单地使用 DateTime.UtcNow
:
private static async Task LongDelay(TimeSpan delay)
{
var start = DateTime.UtcNow;
while (true)
{
var remaining = (delay - (DateTime.UtcNow - start)).TotalMilliseconds;
if (remaining <= 0)
break;
if (remaining > Int16.MaxValue)
remaining = Int16.MaxValue;
await Task.Delay(TimeSpan.FromMilliseconds(remaining));
}
}
从 .NET 6 开始,Task.Delay
method is now 4,294,967,294 milliseconds (0xFFFFFFFE
, or UInt32.MaxValue - 1
), which is approximately 49 days and 17 hours. It is the same limit with the dueTime
/period
arguments of the System.Threading.Timer
构造函数的最大有效 TimeSpan delay
值,Task.Delay
在内部基于该值。
相关 GitHub 问题:Task.Delay
actual accepted delay is twice the documented value