等待任务超时 [英] awaiting task with timeout

查看:71
本文介绍了等待任务超时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个帮助程序方法,该方法允许我传递任意任务和超时.如果任务在超时之前完成,则调用成功委托,否则调用错误委托.该方法如下所示:

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

这篇关于等待任务超时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆