I'm working on a small utility console app, built in C# 7.1 (which has async Main
support).
The app takes one of several input commands and then starts a long-running process which iterates through tens of thousands of items, processing each.
I want to be able to cancel this process at any time (using CTRL+C), although the program should not immediately cancel but instead should finish the current iteration, then stop.
Here's a shortened version of what I have so far.
private static bool _cancel;
private static async Task Main(string[] args)
{
Console.CancelKeyPress += (sender, eventArgs) =>
{
eventArgs.Cancel = true; _logger("Migration will be stopped after the current record has been completed.\n");
_cancel = true;
};
while (!_cancel)
{
var input = Console.ReadLine();
// handle the various input commands
}
}
Inside the methods which run the (optional) long-running process there is logic which checks for this global _cancel
variable:
private static async Task RunPersonMigration(Order order)
{
var nextPerson = // ...
while (nextPerson.IsValid)
{
// business logic
if (_cancel)
{
_logger("Person migration stopped by user.\n");
return;
}
nextPerson = // ...
}
}
However, whenever I hit CTRL+C the Visual Studio debugger asks me to locate an assembly, and it's usually a different assembly each time. For example, I've been asked to locate waithandle.cs and thread.cs. Because I'm unable to locate such files, the running debug process abruptly stops.
I can never see which line is causing the problem, and no amount of breakpoints helps.
Basically, I'm trying to use CTRL+C to exit a long-running process without exiting the console app.
Can anyone tell me how I should correctly handle cancelling a long-running console process at a point of my choosing?
UPDATE:
If I update my CancelKeyPress delegate...
Console.CancelKeyPress += (sender, eventArgs) =>
{
**eventArgs.Cancel = true;** _logger("Migration will be stopped after the current record has been completed.\n");
_cancel = true;
};
then this stops the program from crashing to a close, but I'd still like to be able to trap CTRL+C and use it as a means of exiting the long-running process without exiting the console app itself. Is this even possible?
A working example using a CancellationToken
, which can be passed down to lower levels:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp
{
class Program
{
// Cancellation Tokens - https://learn.microsoft.com/en-us/previous-versions/dd997289(v=vs.110)
private static readonly CancellationTokenSource canToken = new CancellationTokenSource();
static async Task Main(string[] args)
{
Console.WriteLine("Application has started. Ctrl-C to end");
Console.CancelKeyPress += (sender, eventArgs) =>
{
Console.WriteLine("Cancel event triggered");
canToken.Cancel();
eventArgs.Cancel = true;
};
await Worker();
Console.WriteLine("Now shutting down");
await Task.Delay(1000);
}
async static Task Worker()
{
while (!canToken.IsCancellationRequested)
{
// do work
Console.WriteLine("Worker is working");
await Task.Delay(1000); // arbitrary delay
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With