为什么在任务为 运行 时添加 Console.CancelKeyPress 处理程序会阻止调试器 CTRL+C 退出
Why does adding a Console.CancelKeyPress handler block debugger CTRL+C exit when a Task is running
我已经设法在 VS2017 中使用 .Net Framework 4.6 将其简化为最小测试用例 运行ning:
static void Main(string[] args)
{
Console.CancelKeyPress += (o, e) => End(); //some attempt to exit gracefully
Task.Run(() => Task.Delay(100000)).Wait();
}
private static void End()
{
Console.WriteLine("EXITING...");
}
如果我 运行 在调试器中用 Console.CancelKeyPress
注释掉了这个,CTRL+C 强制终止应用程序。使用编写的代码,它输出 "EXITING..." 然后挂起,即使我的事件处理程序没有采取任何措施来防止终止。
如果我从命令行 运行,两个版本都会按预期退出。
我花了一些时间才弄清楚涉及未完成的 Task
但我不知道为什么事件处理程序正在改变行为。谁能弄清楚为什么?这是一些调试器怪癖吗?我在调试器中没有看到任何错误...
这只是一个大胆的猜测,但是您是否检查过将 ConsoleCancelEventArgs 实例 e 的取消 属性 设置为 false 时会发生什么情况?
如果这能解决您的问题,那就太奇怪了,不过它可能有助于缩小问题范围。
PS: 我无法自己测试,因为我目前在 Linux 机器上。
这是一个 visual studio 调试器问题,我认为它的发生是因为 visual studio 正在分别跟踪所有 运行ning 线程,正如你所说你有一个未完成的任务 运行宁在另一个线程。
当您使用 CTRL+C
从命令行终止程序时,命令行会强制您的主线程停止。这样你的子线程也会停止,但是当你 运行 使用 VS-debugger 主线程的应用程序连接到调试器时。
默认情况下,如果您不处理 Console.CancelKeyPress
事件,调试器将释放所有阻塞线程,但如果您想手动控制终止调用则不会。 (I'm not sure why)
如果您想强制调试器终止所有线程,您必须手动执行此操作。
private static void End()
{
Console.WriteLine("EXITING...");
if (Debugger.IsAttached)
Environment.Exit(1); // this is equal to using CTRL+C in the terminal
}
此外,您应该使用取消令牌在应用程序终止前彻底取消您的 运行ning 任务。
class Program
{
private static CancellationTokenSource tokenSource;
static async Task Main(string[] args)
{
tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Console.CancelKeyPress += (o, e) => End();
await Task.Run(() => Task.Delay(100000), token);
}
private static void End()
{
Console.WriteLine("EXITING...");
tokenSource.Cancel();
tokenSource.Dispose();
if (Debugger.IsAttached)
Environment.Exit(1);
}
}
我已经设法在 VS2017 中使用 .Net Framework 4.6 将其简化为最小测试用例 运行ning:
static void Main(string[] args)
{
Console.CancelKeyPress += (o, e) => End(); //some attempt to exit gracefully
Task.Run(() => Task.Delay(100000)).Wait();
}
private static void End()
{
Console.WriteLine("EXITING...");
}
如果我 运行 在调试器中用 Console.CancelKeyPress
注释掉了这个,CTRL+C 强制终止应用程序。使用编写的代码,它输出 "EXITING..." 然后挂起,即使我的事件处理程序没有采取任何措施来防止终止。
如果我从命令行 运行,两个版本都会按预期退出。
我花了一些时间才弄清楚涉及未完成的 Task
但我不知道为什么事件处理程序正在改变行为。谁能弄清楚为什么?这是一些调试器怪癖吗?我在调试器中没有看到任何错误...
这只是一个大胆的猜测,但是您是否检查过将 ConsoleCancelEventArgs 实例 e 的取消 属性 设置为 false 时会发生什么情况?
如果这能解决您的问题,那就太奇怪了,不过它可能有助于缩小问题范围。
PS: 我无法自己测试,因为我目前在 Linux 机器上。
这是一个 visual studio 调试器问题,我认为它的发生是因为 visual studio 正在分别跟踪所有 运行ning 线程,正如你所说你有一个未完成的任务 运行宁在另一个线程。
当您使用 CTRL+C
从命令行终止程序时,命令行会强制您的主线程停止。这样你的子线程也会停止,但是当你 运行 使用 VS-debugger 主线程的应用程序连接到调试器时。
默认情况下,如果您不处理 Console.CancelKeyPress
事件,调试器将释放所有阻塞线程,但如果您想手动控制终止调用则不会。 (I'm not sure why)
如果您想强制调试器终止所有线程,您必须手动执行此操作。
private static void End()
{
Console.WriteLine("EXITING...");
if (Debugger.IsAttached)
Environment.Exit(1); // this is equal to using CTRL+C in the terminal
}
此外,您应该使用取消令牌在应用程序终止前彻底取消您的 运行ning 任务。
class Program
{
private static CancellationTokenSource tokenSource;
static async Task Main(string[] args)
{
tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Console.CancelKeyPress += (o, e) => End();
await Task.Run(() => Task.Delay(100000), token);
}
private static void End()
{
Console.WriteLine("EXITING...");
tokenSource.Cancel();
tokenSource.Dispose();
if (Debugger.IsAttached)
Environment.Exit(1);
}
}