我有一个控制台应用程序,我希望在监听事件的同时始终保持打开状态。我已经测试Thread.Sleep(Timeout.Infinite);
并且while (true) { }
两者都允许在保持控制台应用程序打开的同时引发事件。有一个我应该使用的吗?如果线程正在休眠,有什么我不应该做的,比如修改类范围内声明的静态集合?
6 回答
我建议使用ManualResetEvent
(或其他WaitHandle
),并调用ManualResetEvent.WaitOne。
这将具有与永远睡眠类似的效果,除了它为您提供了一种在需要时(通过调用事件)退出无限“块”的干净方式。Set()
使用while(true)
会消耗 CPU 周期,所以这绝对是要避免的。
有什么我不应该做的,比如修改在类范围内声明的静态集合?
一般来说,没有。由于您的线程将被阻止,因此使用共享数据不应该有任何同步问题(前提是集合中的项目没有特定要求,例如必须在具有适当同步上下文的线程上使用的用户界面元素.)
从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.Delay
inf-sleep,try-catch 效率较低并且会混淆队友;使用其他任务合作机制):
try
{
await Task.Delay(Timeout.Infinite, Cts.Token).ConfigureAwait(false);
}
catch(TaskCanceledException) { /* Ignored */ }
我认为电话
while (true) { ... }
是计算密集型的,因为线程永远不会停止,whearas
Thread.Sleep(Timeout.Infinite);
实际上在 OS 本机调度程序的帮助下使线程进入睡眠状态。然后线程实际上停止了,所以我想它对计算的要求不高。
是的,
在以更智能的方式工作时while(true)
消耗 CPU :该函数将当前执行上下文置于睡眠状态;它通过调用系统调用来调用内核睡眠函数,该函数以原子方式
(a) 设置唤醒计时器
(b) 将当前进程标记为睡眠
(c) 等待直到唤醒计时器触发或发生中断 sleep()
sleep()
如果调用sleep()
,CPU 可以做其他工作。
sleep()
这就是有用的原因之一。
一个有用的链接 -使用睡眠时要小心
调用 Thread.Sleep 方法会导致当前线程在您传递给该方法的毫秒数或时间间隔内立即阻塞,并将其时间片的剩余部分交给另一个线程。