【发布时间】:2012-11-04 13:53:46
【问题描述】:
我有一个控制台应用程序,我希望在监听事件的同时始终保持打开状态。我已经测试了Thread.Sleep(Timeout.Infinite); 和while (true) { } 并且都允许在保持控制台应用程序打开的同时引发事件。有一个我应该使用的吗?如果线程在休眠,有什么我不应该做的,比如修改类范围内声明的静态集合?
【问题讨论】:
我有一个控制台应用程序,我希望在监听事件的同时始终保持打开状态。我已经测试了Thread.Sleep(Timeout.Infinite); 和while (true) { } 并且都允许在保持控制台应用程序打开的同时引发事件。有一个我应该使用的吗?如果线程在休眠,有什么我不应该做的,比如修改类范围内声明的静态集合?
【问题讨论】:
从 C# 7.1 开始,您可以使 Main 方法异步。这意味着您可以将 Main 方法作为 Task 异步挂起,而不是使用忙等待或线程锁定睡眠。这样,运行 Main 的线程就不会被锁定。并且使用Cts.Cancel(),您可以轻松释放主任务以退出应用程序(不允许其他线程/任务完成工作)。
static readonly CancellationTokenSource Cts = new CancellationTokenSource();
static async Task Main(string[] args)
{
/* your code here */
// Task running Main is efficiently suspended (no CPU use) forever until Cts activated with Program.Cts.Cancel(); (thread-safe) from anywhere.
await Task.Delay(Timeout.Infinite, Cts.Token).ConfigureAwait(false);
}
自2020 年,C# 9.0 以来,整个Program.cs 文件内容实际上可以像这样:
using System.Threading;
using System.Threading.Tasks;
CancellationTokenSource Cts = new();
/* your code here */
// Task running Main is efficiently suspended (no CPU use) forever until
// Cts activated with Program.Cts.Cancel(); (thread-safe) from anywhere.
await Task.Delay(Timeout.Infinite, Cts.Token).ConfigureAwait(false);
如果无限睡眠取消是该案例的正常代码流,您可以尝试捕获 TaskCanceledException(当您尝试等待/与其他任务同步时,请不要使用 Task.Delayinf-sleep , try-catch 效率较低,会混淆队友;使用其他机制进行任务合作):
try
{
await Task.Delay(Timeout.Infinite, Cts.Token).ConfigureAwait(false);
}
catch(TaskCanceledException) { /* Ignored */ }
【讨论】:
Cts.Cancel(); docs.microsoft.com/en-us/dotnet/api/…
调用 Thread.Sleep 方法会导致当前线程在您传递给该方法的毫秒数或时间间隔内立即阻塞,并将其时间片的剩余部分交给另一个线程。
【讨论】:
是的,
while(true) 消耗 CPU,而 sleep() 以更智能的方式工作:
sleep() 函数使当前执行上下文进入休眠状态;它通过调用系统调用来自动调用内核睡眠函数
(a) 设置唤醒定时器
(b) 将当前进程标记为休眠
(c) 等待直到唤醒定时器触发或发生中断
如果你调用sleep(),CPU 可以做其他工作。
这就是sleep() 有用的原因之一。
一个有用的链接 - Be careful when using Sleep
【讨论】:
我认为电话
while (true) { ... }
是计算密集型的,因为线程永远不会停止,whearas
Thread.Sleep(Timeout.Infinite);
实际上是在 OS 本机调度程序的帮助下让线程进入睡眠状态。然后线程实际上停止了,所以我想它对计算的要求不那么高。
【讨论】:
与while(true)... 不同,Thread.Sleep 不使用 CPU 周期,因此从这个意义上说,睡眠效率更高。一般来说,强烈建议不要在spinlocks 之外使用Busy Waiting。
如果线程正在休眠,有什么我不应该做的吗?
由于您的线程在进入 Thread.Sleep 时被阻止,因此您希望对其资源做的任何事情都是公平的游戏。
【讨论】:
我建议使用ManualResetEvent(或其他WaitHandle),并致电ManualResetEvent.WaitOne。
这将产生与永远睡眠类似的效果,除了它为您提供了一种在需要时退出无限“块”的干净方式(通过在事件上调用Set())。
使用while(true) 会消耗CPU 周期,所以绝对要避免。
有什么我不应该做的,比如修改类范围内声明的静态集合?
一般来说,不会。由于您的线程将被阻止,因此使用共享数据不应该有任何同步问题(前提是集合中的项目没有特定要求,例如必须在具有适当同步上下文的线程上使用的用户界面元素.)
【讨论】: