等待超时的任务

use*_*032 5 .net c# task async-await

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

    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();
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

现在这似乎在大部分时间都有效,但我也想写一些测试以确保.令我意外的是,此测试失败了,并调用错误委托而不是成功:

        var taskToAwait = Task.Delay(1);
        var successCalled = false;

        await TaskHelper.AwaitWithTimeout(taskToAwait, 10, () => successCalled = true, null);

        Assert.IsTrue(successCalled);
Run Code Online (Sandbox Code Playgroud)

但是,此测试为绿色:

        var taskToAwait = Task.Run(async () =>
        {
            await Task.Delay(1);
        });

        var successCalled = false;

        await TaskHelper.AwaitWithTimeout(taskToAwait, 10, () => successCalled = true, null);

        Assert.IsTrue(successCalled);
Run Code Online (Sandbox Code Playgroud)

我如何使两个测试都变绿?我对Task.WhenAny的使用是否不正确?

Sri*_*vel 11

计时器不准确.默认情况下,它们的准确度约为15毫秒.低于此值的任何内容都将在15毫秒间隔内触发.参考相关答案.

鉴于您有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);
}
Run Code Online (Sandbox Code Playgroud)

永不停止任务使用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);
}
Run Code Online (Sandbox Code Playgroud)

另外我建议你不要使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

注意,上述方法是一种扩展方法; 所以你可以用任务实例调用它.

await taskToAwait.AwaitWithTimeout(10, () => { }, ()=> errorCalled = true);//No nulls, just empty delegate
Run Code Online (Sandbox Code Playgroud)