Firstly: never Wait()
on tasks (unless you know they have already finished); use await
. As for the timeout: CancellationTokenSource
can be made to time out after a delay, as below.
Note that cancellation-tokens do not interrupt code - you (or other code, as in the case of Task.Delay
) need to either check for cancellation (for example, ThrowIfCancellationRequested()
), or you need to use Register(...)
to add a callback that will be invoked when it is cancelled.
using System;
using System.Threading;
using System.Threading.Tasks;
static class P
{
public static async Task Main()
{
Console.WriteLine("starting app");
try
{
Console.WriteLine("before");
await DoStuffAsync(1000);
Console.WriteLine("after");
}
catch
{
Console.WriteLine("TIMEOUT");
}
Console.WriteLine("Main finished wait 5 sec now");
await Task.Delay(5000);
Console.WriteLine("Closing app now");
}
public static async Task DoStuffAsync(int time)
{
using var cts = new CancellationTokenSource(time); // assuming here that "time" is milliseconds
Task real = Task.Run(async () =>
{
await Task.Delay(2000, cts.Token);
Console.WriteLine("Task Running");
await Task.Delay(2000, cts.Token);
Console.WriteLine("still running");
await Task.Delay(2000, cts.Token);
Console.WriteLine("Not dead yet");
await Task.Delay(2000, cts.Token);
Console.WriteLine("Task done");
await Task.Delay(2000, cts.Token);
});
bool success;
try
{
await real;
success = true;
}
catch (OperationCanceledException)
{
success = false;
}
if (success)
{
Console.WriteLine("Task finished in time");
}
else
{
Console.WriteLine("Task tool too long");
}
}
}
Note that you can also pass a cancellation-token into Task.Run
, but that simply gets checked before executing the callback - and there isn't usually a significant delay on that, so... it isn't usually worth it.
Additional note: if you want to be sure exactly what was cancelled (i.e. is the OperationCanceledException
coming from cts
):
catch (OperationCanceledException cancel) when (cancel.CancellationToken == cts.Token)
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…