Reputation: 33
I am trying to stop a task in C# after a certain period of time.
For my code: the Task.Delay().Wait() should just represent some work that the Task does.
My Code:
public static void Main()
{
Console.WriteLine("starting app");
try
{
Console.WriteLine("before");
DoStuff(1000);
Console.WriteLine("after");
}
catch
{
Console.WriteLine("TIMEOUT");
}
Console.WriteLine("Main finished wait 5 sec now");
Task.Delay(10000).Wait();
Console.WriteLine("Closing app now");
}
public static async Task DoStuff(int time)
{
Task real = Task.Run(()=>
{
Task.Delay(2000).Wait();
Console.WriteLine("Task Running");
Task.Delay(2000).Wait();
Console.WriteLine("still running");
Task.Delay(2000).Wait();
Console.WriteLine("Not dead yet");
Task.Delay(1000).Wait();
Console.WriteLine("Task done");
Task.Delay(5000);
});
bool success = real.Wait(time);
if ( success )
{
Console.WriteLine("Task finished in time");
await real;
}
else
{
Console.WriteLine("Task did not finish");
real.Dispose();
throw new TimeoutException("The task took too long");
}
}
I already tried it with the cancellation token, but I do not have a loop to check the token every iteration. So I tried to do it with the Task.Wait(time)
and I get the right message, but the task does not stop after using the Task.Dispose()
method. I get the following output:
So I get the current output but the task continues to run in the back.. Any ideas on how to solve this?
Upvotes: 2
Views: 3236
Reputation: 1062600
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)
Upvotes: 2