C# 正在等待超时的任务

C# 正在等待超时的任务,c#,.net,async-await,task,C#,.net,Async Await,Task,我正在尝试编写一个助手方法,它允许我传入任意任务和超时。如果任务在超时之前完成,则调用成功委托,否则调用错误委托。方法如下所示: public static async Task AwaitWithTimeout(Task task, int timeout, Action success, Action error) { if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {

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

    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.When的使用是否不正确?

计时器不准确。默认情况下,它们的准确度约为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);
}
对于永不结束的任务,请使用
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);
}
请注意,上述方法是一种扩展方法;因此,您可以使用任务实例调用它

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

Delay(1)
非常短(特别是在调试时)-在单独的线程上启动它可以使它足够长,使您的测试大部分时间都能通过。尝试使用更长的延迟或更好的任务,允许在测试中手动同步完成。谢谢。对于如何测试错误场景,您有什么建议吗?也就是说,输入一个从未完成的任务。现在,我仍然使用Task.Delay()来完成这个任务(只是将延迟设置得很高,超时设置得有点低)@user1202032当然。更新了我的答案,以解决永无止境的任务。虽然核心概念(使用
task.WhenAny
等待两个任务)是正确的,但它周围的API感觉脏兮兮的<代码>行动如果你需要从中连接另一个
任务
,那么成功就是一团糟-你最终会得到丑陋的捕获。我只需在超时场景中抛出一个
TimeoutException
,并将成功完成视为成功完成。在性能关键的情况下,
任务
返回类型也可以工作(其中
true
表示成功完成,
false
表示超时)。然后还有超时任务最终完成的处理(此处讨论:)。当然,别忘了
ConfigureAwait(false)
——绝对没有理由不在helper或library方法中使用它。@KirillShlenskiy是的,这正是我的想法<代码>任务比学员更有意义。更好的时间例外。我把它留给OP。
await taskToAwait.AwaitWithTimeout(10, () => { }, ()=> errorCalled = true);//No nulls, just empty delegate