我正在尝试编写一个帮助程序方法,该方法允许我传递任意任务和超时.如果任务在超时之前完成,则调用成功委托,否则调用错误委托.该方法如下所示:
I'm trying to write a helper method which allows me to pass in an arbitrary task and a timeout. If the task completes before the timeout, a success delegate is called, otherwise an error delegate is called. The method looks like this:
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(); } } }现在这个似乎大部分时间都可以工作,但是我也想编写一些测试来确保.令我惊讶的是,该测试失败了,并调用了错误委托而不是成功:
Now this seems to work most of the time, but i wanted to write some tests as well to make sure. This test, to my surprise fails, and calls the error delegate instead of the success:
var taskToAwait = Task.Delay(1); var successCalled = false; await TaskHelper.AwaitWithTimeout(taskToAwait, 10, () => successCalled = true, null); Assert.IsTrue(successCalled);但是,此测试是绿色的:
This test, however, is green:
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的用法不正确吗?
How do i make both tests green? Is my usage of Task.WhenAny incorrect?
推荐答案计时器不准确.默认情况下,它们的精度约为15毫秒.低于此值的任何时间都将在15ms的间隔内触发. 参阅相关答案.
Timers are inaccurate. By default their accuracy is around 15 ms. Anything lower than that will trigger in 15ms interval. Refer related answer.
假设您有1毫秒计时器和10毫秒计时器;两者大致相等,所以您在那里得到的结果不一致.
Given that you have 1ms timer and 10ms timer; both are roughly equal so you get inconsistent results there.
包装在Task.Run中并声称可以正常工作的代码只是一个巧合.当我尝试几次时,结果不一致.有时由于提到的相同原因而失败.
The code you wrapped in Task.Run and claims to be working is just a coincidence. When I tried several times, results are inconsistent. It fails sometimes for the same reason mentioned.
最好增加超时时间,或者只传递已经完成的任务.
You're better off increasing the timeout or just pass in a already completed task.
例如,以下测试应始终通过.请记住,您的测试应该一致而不易碎.
For example following test should consistently pass. Remember that your test should be consistent not brittle.
[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并且不要设置其结果.
For never ending task use TaskCompletionSource and don't set its result.
[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.您可以只将空的委托作为参数传递.然后,您就不想在整个代码库中散布null检查.
Also I recommend you to avoid using null. You can just pass the empty delegate as a parameter. Then you don't want to have null checks scattered all over your codebase.
我将辅助方法编写为:
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(); } }请注意,上述方法是扩展方法;因此您可以使用任务实例来调用它.
Note that above method is an extension method; so you can call it with task instance.
await taskToAwait.AwaitWithTimeout(10, () => { }, ()=> errorCalled = true);//No nulls, just empty delegate更多推荐
等待任务超时
发布评论