C# 如何等待 X 秒让任务完成

C# How to wait X seconds for a task to complete

我想在不阻塞线程的情况下等待任务完成指定的时间,并根据任务是否完成执行某些工作。
例如在特定时间内等待 HTTP 请求。

计算结束时间,然后等到那个时间过去。

public void TrySomething(int sec)
{
    DateTime endTime = DateTime.Now.AddSeconds(sec);
    while (DateTime.Now < endTime)
    {
         // do useful stuff
    }
}

Stopwatch 是去这里的方法:

public void TrySomething(int sec)
{
    var sw = Stopwatch.StartNew();
    while (sw.Elapsed.TotalSeconds < (double)sec)
    {
         // do useful stuff
    }
}

甚至:

public void TrySomething(int sec)
{
    var sw = Stopwatch.StartNew();
    while (true)
    {
        var result = ...
        // do useful stuff
        if (sw.Elapsed.TotalSeconds >= (double)sec)
        {
            return result;
        }
    }
}

一般情况下,您可以使用 CancellationTokenSource,如果期限已过,它会将令牌 IsCancellationRequested 属性 设置为 true。因此,您的代码将是。

public bool TrySomething(int sec)
{
    var ct = new CancellationTokenSource(TimeSpan.FromSeconds(sec)).Token;
    var valueToReturne=false;
    while (!ct.IsCancellationRequested)
    {
        if (!MyCondition)
                    continue;
        else
                    valueToReturne = true;
    }
    return valueToReturne;
}

但是,与其他解决方案类似,这是一个繁忙的循环,意味着会阻塞您的主线程。

如果你想让你停止编程而不响应你需要让你的方法异步,例如通过将 TrySomething 包装在 Task.Run 中,例如

var ct = new CancellationTokenSource(TimeSpan.FromSeconds(sec)).Token;
await Task.Run(() => TrySomething(ct), ct);

public bool TrySomething(CancellationToken ct)
{
    var valueToReturne=false;
    while (!ct.IsCancellationRequested)
    {
        if (!MyCondition)
                    continue;
        else
                    valueToReturne = true;
    }
    return valueToReturne;
}

但是,这应该只用于 CPU-bound 工作。但是你说你想检查你的互联网连接,这是 IO-bound 工作。您仍然可以使用 Task.Run,但在这种情况下,这是非常不受欢迎的。最佳解决方案是使用适当的异步方法进行互联网连接检查,例如如果您使用 Ping.Send 检查互联网连接,您应该使用 Ping.SendAsync 等。

在这种情况下,您的代码将是:

var ct = new CancellationTokenSource(TimeSpan.FromSeconds(sec)).Token;
await TrySomething(ct);

public async Task<bool> TrySomethingAsync(CancellationToken ct)
{
    var valueToReturne=false;
    while (!ct.IsCancellationRequested)
    {
        if (!await MyConditionAsync(ct))
                    continue;
        else
                    valueToReturne = true;
    }
    return valueToReturne;
}