I wrote the following C# code in .NET 6 in a simple console application. It captures "Interrupt" signal (i.e. Ctrl+C) and signals cancellation token source to cancel its token.
Then in Main
method, I want the thread to exit. Why does the following code not work as expected i.e. the Main
method does not exit and I don't see the message "Exiting..."
namespace ConsoleApp1
{
public class Program
{
private static CancellationTokenSource cancellationTokenSource =
new CancellationTokenSource();
public static void Main(string[] args)
{
Console.CancelKeyPress += new ConsoleCancelEventHandler(InterruptHandler);
Task.Delay(Timeout.Infinite, cancellationTokenSource.Token).Wait();
Console.WriteLine("Exiting...");
}
private static void InterruptHandler(object? sender, ConsoleCancelEventArgs e)
{
cancellationTokenSource.Cancel();
}
}
}
Then I changed the main method to this, still same thing happened.
public static void Main(string[] args)
{
Console.CancelKeyPress += new ConsoleCancelEventHandler(InterruptHandler);
while (cancellationTokenSource.IsCancellationRequested == false)
Thread.Sleep(1000);
Console.WriteLine("Exiting...");
}
As I found out, Ctrl+C terminates the process, so your app just ends after pressing the combination. In order to change that behaviour you need to set ConsoleCancelEventArgs.Cancel
property to true
.
This will change behaviour, but it won't be exactly as you expect, as cancelling task is just interrupting it by throwing exception, so you'd need to add handling of that exception, please see below example:
var cts = new CancellationTokenSource();
Console.CancelKeyPress += Console_CancelKeyPress;
try
{
//await Task.Delay(10 * 1000, cts.Token);
Task.Delay(60 * 1000, cts.Token).Wait();
Console.WriteLine("After waiting");
}
// This will be thrown when you use .Wait() method
catch(AggregateException aggEx)
{
var tcEx = aggEx.InnerExceptions.FirstOrDefault(x => x is TaskCanceledException);
if (tcEx != null)
{
Console.WriteLine(tcEx.ToString());
}
}
// This will be thrown when you await the task
catch (TaskCanceledException cacnelledEx)
{
Console.WriteLine(cacnelledEx.ToString());
}
void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
Console.WriteLine("Cancelling operation");
cts.Cancel();
e.Cancel = true;
}
UPDATE
As suggested, you could simplify the code by replacing Wait()
method with GetAwaiter().GetResult()
, which would throw TaskCancelledException
instead of AggregateException
:
try
{
//await Task.Delay(10 * 1000, cts.Token);
Task.Delay(60 * 1000, cts.Token).GetAwaiter().GetResult();
Console.WriteLine("After waiting");
}
catch (TaskCanceledException cacnelledEx)
{
Console.WriteLine(cacnelledEx.ToString());
}