创建一个在按下 Ctrl+C 时输出一行内容并退出的控制台应用程序。

huangapple go评论74阅读模式
英文:

Creating a console app that writes line of output and exits upon Ctrl+C

问题

我想创建一个控制台应用程序,在使用 Ctrl+C 终止时,在输出的最后一行显示一个“摘要语句”,但我在使其正常工作方面遇到了问题。这可能部分是因为我试图创建的应用程序在一个 while(true) 循环中不断写入输出。我可以通过一个简单的示例来最好地说明这个问题:

假设我有一个控制台应用程序,它不断输出当前时间:

while (true)
{
    var now = DateTime.Now;
    Console.Write(now);
    Console.Write(" ");
    Console.Write(now.Millisecond);
    Console.CursorLeft = 0; // 覆盖同一行
}

这可以正常工作,我可以使用 Ctrl+C 终止应用程序:

23/02/2023 23:07:14 658

现在,当我按下 Ctrl+C 时,我希望文本 Goodbye! 作为输出的最后一行显示出来:

23/02/2023 23:09:56 441
Goodbye!

但是,我在使这个正常工作方面遇到了问题。以下是我的第一次尝试:

Console.CancelKeyPress += OnCancel;

while (true)
{
    var now = DateTime.Now;
    Console.Write(now);
    Console.Write(" ");
    Console.Write(now.Millisecond);
    Console.CursorLeft = 0; // 覆盖同一行
}

void OnCancel(object? sender, ConsoleCancelEventArgs e)
{
    Console.WriteLine("Goodbye");
}

然而,这导致了混乱的输出,例如:

Goodbye023 23:14:55 670
23/02/2023 23:14:55

我认为这是因为 Console.CancelKeyPress 事件处理程序在一个单独的工作线程上执行,并且对 Console.WriteLine("Goodbye"); 的调用可以在 while 循环的任何点发生。理想情况下,CancelKeyPress 事件处理程序不应该在 while 循环的当前迭代完成之前运行。我不确定如何以线程安全的方式实现这一点。

我尝试了不同的方法,我不会列出它们的代码,但总结一下,我尝试了以下方法,但都没有成功:

  1. 在顶层范围中定义一个布尔变量 cancelRequested,将 while 循环条件设置为 !cancelRequested;在 OnCancel 中将变量设置为 true;在循环后使用 Console.WriteLine("Goodbye!")。这会导致在 Console.WriteLine("Goodbye!") 行上阻塞(死锁?)。

  2. 类似于上述方法,但使用 CancellationTokenSource 而不是布尔变量。观察到相同的死锁行为,因此我认为这是等效的。

英文:

I want to create a Console app that outputs a 'summary statement' as the last line of output, when it is terminated using <kbd>Ctrl+C</kbd>, but I'm having trouble getting this working. This may be in part due to app I'm trying to create is constantly writing output in a while(true) loop. I can best illustrate the problem with an simple example:

Let's say I have a console app that constantly outputs the current time:

while (true)
{
    var now = DateTime.Now;
    Console.Write(now);
    Console.Write(&quot; &quot;);
    Console.Write(now.Millisecond);
    Console.CursorLeft = 0; // Overwrite the same line
}

This works and I can terminate the app with <kbd>Ctrl+C</kbd>:

23/02/2023 23:07:14 658

Now when I press <kbd>Ctrl+C</kbd> I want the text Goodbye! displayed as the final line of output:

23/02/2023 23:09:56 441
Goodbye!

However, I'm having trouble getting this working. Here's my first attempt:

Console.CancelKeyPress += OnCancel;

while (true)
{
    var now = DateTime.Now;
    Console.Write(now);
    Console.Write(&quot; &quot;);
    Console.Write(now.Millisecond);
    Console.CursorLeft = 0; // Overwrite the same line
}

void OnCancel(object? sender, ConsoleCancelEventArgs e)
{
    Console.WriteLine(&quot;Goodbye&quot;);
}

However, this results 'garbled' output, for example:

Goodbye023 23:14:55 670
23/02/2023 23:14:55

I think this is happening because the Console.CancelKeyPress event handler is executed on a separate worker thread and the call to Console.WriteLine(&quot;Goodbye&quot;); can happen at any point within the while loop. Ideally the CancelKeyPress event handler isn't ran until current iteration of the while loop is finished. I'm not sure how to do this in a thread-safe way.

I've tried different approaches, and I won't listed code for them, but in summary all of the following I've tried to no avail:

  1. Defined a boolean variable cancelRequested in the top scope and set while
    loop condition to !cancelRequested; In OnCancel set the var to true;
    Console.WriteLine(&quot;Goodbye!&quot;) after the loop. This blocks on the
    Console.WriteLine(&quot;Goodbye!&quot;) line (deadlock?).
  2. Similar to above but used CancellationTokenSource instead of a boolean variable.
    Same deadlock behavior is observed, so I guess this is equivalent?

答案1

得分: 2

正如评论中@NetMage所指出的,我第一次尝试使用布尔变量来解决乱码文本问题的尝试并没有成功,因为通常按下<kbd>Ctrl+C</kbd>在CancelKeyPress结束时会终止应用程序,我没有意识到我需要通过将ConsoleCancelEventArgs.Cancel设置为true来抑制默认行为。现在我知道了这一点,我有一个有效的解决方案:

Console.CancelKeyPress += OnCancel;
bool cancelRequested = false;

while (!cancelRequested)
{
    var now = DateTime.Now;
    Console.Write(now);
    Console.Write(" ");
    Console.Write(now.Millisecond);
    Console.CursorLeft = 0; // 覆盖同一行
}
    
Console.WriteLine();
Console.WriteLine("Goodbye");

void OnCancel(object? sender, ConsoleCancelEventArgs e)
{
    e.Cancel = true;        // 阻止取消按键终止应用程序
    cancelRequested = true; // 通知主循环请求取消
}
英文:

As pointed out by @NetMage in the comments, my first attempt to solve the issue with the garbled text using a boolean variable was not working because normally pressing <kbd>Ctrl+C</kbd> terminates the app at the end of the CancelKeyPress and hadn't realised that I need to supress the default behavior by setting ConsoleCancelEventArgs.Cancel to true. Now that I know this, I have a working solution:

Console.CancelKeyPress += OnCancel;
bool cancelRequested = false;

while (!cancelRequested)
{
    var now = DateTime.Now;
    Console.Write(now);
    Console.Write(&quot; &quot;);
    Console.Write(now.Millisecond);
    Console.CursorLeft = 0; // Overwrite the same line
}
    
Console.WriteLine();
Console.WriteLine(&quot;Goodbye&quot;);

void OnCancel(object? sender, ConsoleCancelEventArgs e)
{
    e.Cancel = true;        // Stops cancel key press from terminating the app
    cancelRequested = true; // Signal the main loop the request to cancel
}

huangapple
  • 本文由 发表于 2023年2月24日 07:49:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/75551418.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定