等待任务超时
awaiting task with timeout
我正在尝试编写一个辅助方法,它允许我传入任意任务和超时。如果任务在超时之前完成,则调用成功委托,否则调用错误委托。该方法如下所示:
public static async Task AwaitWithTimeout(Task task, int timeout, Action success, Action error)
{
if (await Task.WhenAny(task, Task.Delay(timeout)) == task)
{
if (success != null)
{
success();
}
}
else
{
if (error != null)
{
error();
}
}
}
现在这个似乎大部分时间都可以工作,但我也想写一些测试来确保。令我惊讶的是,这个测试失败了,并调用错误委托而不是成功:
var taskToAwait = Task.Delay(1);
var successCalled = false;
await TaskHelper.AwaitWithTimeout(taskToAwait, 10, () => successCalled = true, null);
Assert.IsTrue(successCalled);
然而,这个测试是绿色的:
var taskToAwait = Task.Run(async () =>
{
await Task.Delay(1);
});
var successCalled = false;
await TaskHelper.AwaitWithTimeout(taskToAwait, 10, () => successCalled = true, null);
Assert.IsTrue(successCalled);
如何使两个测试都变为绿色?我对 Task.WhenAny 的用法不正确吗?
计时器不准确。默认情况下,它们的精度约为 15 毫秒。任何低于该值的值都会以 15 毫秒的间隔触发。 Refer related answer.
鉴于您有 1ms 计时器和 10ms 计时器;两者大致相等,因此您会得到不一致的结果。
您封装在 Task.Run
中并声称有效的代码纯属巧合。当我尝试多次时,结果不一致。它有时会因上述相同原因而失败。
你最好增加超时时间或只传递一个已经完成的任务。
例如,以下测试应该始终通过。请记住,您的测试应该是一致的而不是脆弱的。
[Test]
public async Task AwaitWithTimeout_Calls_SuccessDelegate_On_Success()
{
var taskToAwait = Task.FromResult(0);
var successCalled = false;
await TaskHelper.AwaitWithTimeout(taskToAwait, 10, () => successCalled = true, ()=>{ });
Assert.IsTrue(successCalled);
}
对于永无止境的任务,请使用 TaskCompletionSource
并且不要设置其结果。
[Test]
public async Task AwaitWithTimeout_Calls_ErrorDelegate_On_NeverEndingTask()
{
var taskToAwait = new TaskCompletionSource<object>().Task;
var errorCalled = false;
await TaskHelper.AwaitWithTimeout(taskToAwait, 10, () => { }, ()=> errorCalled = true);
Assert.IsTrue(errorCalled);
}
我还建议您避免使用 null
。您可以只将空委托作为参数传递。那么您不希望空检查散布在您的代码库中。
我会将辅助方法编写为:
public static async Task AwaitWithTimeout(this Task task, int timeout, Action success, Action error)
{
if (await Task.WhenAny(task, Task.Delay(timeout)) == task)
{
success();
}
else
{
error();
}
}
注意上面的方法是扩展方法;所以你可以用任务实例调用它。
await taskToAwait.AwaitWithTimeout(10, () => { }, ()=> errorCalled = true);//No nulls, just empty delegate
我正在尝试编写一个辅助方法,它允许我传入任意任务和超时。如果任务在超时之前完成,则调用成功委托,否则调用错误委托。该方法如下所示:
public static async Task AwaitWithTimeout(Task task, int timeout, Action success, Action error)
{
if (await Task.WhenAny(task, Task.Delay(timeout)) == task)
{
if (success != null)
{
success();
}
}
else
{
if (error != null)
{
error();
}
}
}
现在这个似乎大部分时间都可以工作,但我也想写一些测试来确保。令我惊讶的是,这个测试失败了,并调用错误委托而不是成功:
var taskToAwait = Task.Delay(1);
var successCalled = false;
await TaskHelper.AwaitWithTimeout(taskToAwait, 10, () => successCalled = true, null);
Assert.IsTrue(successCalled);
然而,这个测试是绿色的:
var taskToAwait = Task.Run(async () =>
{
await Task.Delay(1);
});
var successCalled = false;
await TaskHelper.AwaitWithTimeout(taskToAwait, 10, () => successCalled = true, null);
Assert.IsTrue(successCalled);
如何使两个测试都变为绿色?我对 Task.WhenAny 的用法不正确吗?
计时器不准确。默认情况下,它们的精度约为 15 毫秒。任何低于该值的值都会以 15 毫秒的间隔触发。 Refer related answer.
鉴于您有 1ms 计时器和 10ms 计时器;两者大致相等,因此您会得到不一致的结果。
您封装在 Task.Run
中并声称有效的代码纯属巧合。当我尝试多次时,结果不一致。它有时会因上述相同原因而失败。
你最好增加超时时间或只传递一个已经完成的任务。
例如,以下测试应该始终通过。请记住,您的测试应该是一致的而不是脆弱的。
[Test]
public async Task AwaitWithTimeout_Calls_SuccessDelegate_On_Success()
{
var taskToAwait = Task.FromResult(0);
var successCalled = false;
await TaskHelper.AwaitWithTimeout(taskToAwait, 10, () => successCalled = true, ()=>{ });
Assert.IsTrue(successCalled);
}
对于永无止境的任务,请使用 TaskCompletionSource
并且不要设置其结果。
[Test]
public async Task AwaitWithTimeout_Calls_ErrorDelegate_On_NeverEndingTask()
{
var taskToAwait = new TaskCompletionSource<object>().Task;
var errorCalled = false;
await TaskHelper.AwaitWithTimeout(taskToAwait, 10, () => { }, ()=> errorCalled = true);
Assert.IsTrue(errorCalled);
}
我还建议您避免使用 null
。您可以只将空委托作为参数传递。那么您不希望空检查散布在您的代码库中。
我会将辅助方法编写为:
public static async Task AwaitWithTimeout(this Task task, int timeout, Action success, Action error)
{
if (await Task.WhenAny(task, Task.Delay(timeout)) == task)
{
success();
}
else
{
error();
}
}
注意上面的方法是扩展方法;所以你可以用任务实例调用它。
await taskToAwait.AwaitWithTimeout(10, () => { }, ()=> errorCalled = true);//No nulls, just empty delegate