首先,我不是问C# - Alternative to Thread.Sleep?或Alternative to Thread.Sleep in C#?这个问题 . 我不认为我使用它不正确,需要一个真正的替代特定情况 .
在代码分析运行期间,我看到了一个令人惊讶的违规行为:
Thread.Sleep()的使用是有缺陷设计的标志 .
这种违规导致Peter Richie's article为什么这究竟构成了糟糕的设计 .
我们都知道线程创建很昂贵,线程中的阻塞意味着池上的争用 . 我们也知道每个线程将分配一个兆内存,因此它应该有一个短的寿命,在UI上阻塞是邪恶的,使用睡眠时间是不可靠的等等 . 这导致我的观点,如果你真的需要执行睡觉,如果不是Thread.Sleep你应该使用什么?
Peter继续提到零睡眠是Thread.Sleep唯一正确使用放弃线程的时间片并允许其他线程处理 . 然后更可怕的是,这仅仅是因为非托管线程的限制,如果在CLR中重新实现将在您的应用程序中产生使用Thread.Sleep的副作用 . 事实上,常见的不良用法的所有要点都是不良用法的好例子 .
我在 生产环境 代码中有以下情况,它使用Thread.Sleep非常成功:
-
等待文件锁被操作系统放弃(捕获文件锁定问题, wait for a second ,再试一次,过一会儿就放弃) .
-
杀死一个进程并等待它不显示在进程列表中(杀死它,检查它没有运行, wait for a second ,检查它是否仍在运行,强行关闭) .
-
等待复制缓冲区刷新(检查文件大小,尝试访问它, wait ,检查大小是否已更改) .
在这种情况下不使用Thread.Sleep,我还有其他选择吗?紧密的循环往往会使事情变得更糟,我不相信这会使它的使用成为“设计缺陷”,尤其是因为UI上没有任何东西,只有后台线程 . 在多线程环境中等待其他事物以及影响代码的外部因素,这只是软件的本质,有时您需要等待......
3 回答
在我的一个项目中,我使用了2个线程,我遇到了问题,UI冻结thnx到Thread.Sleep ....这解决了我的问题:
嗯,你说的大部分都是 . 引用“我们都知道线程创建是昂贵的,并且线程中的阻塞意味着池上的争用”,因此您甚至可以理解使用线程池的内容 .
您还了解阻止UI线程是不好的 .
再次查看线程池模型:您有一个线程池,可能每个处理器一个,然后将任务传递给它们 . 它会阻止其中一个线程有什么意义?如果现在没有工作要做,那么它应该只是进行一项不同的任务 .
所以,直接回答你的问题“如果你真的需要进行睡眠,那么你应该使用什么,如果不是Thread.Sleep?”,在现代精心设计的程序中你永远不需要做它,你只需为后者安排任务 .
您应该将池中的线程(就像系统中的处理器一样)视为资源,这些资源应该在不需要时释放给其他人 .
在你的例子中,你有点过分参与命令式编程范式 .
你不要想象为什么你需要这个,但如果必须等待,那是因为你有时间工作,你的功能"continuation" . 你应该为这个"continuation"设置一个计时器 .
文件示例应该有其他机制,如果它们没有....这将是良好的操作系统设计 . 例如,等待缓冲区刷新的唯一安全方法是操作系统原语,就像fsync一样 .
如果有人写入文件然后从文件读取另一个文件,则需要同步机制,而不是定时等待(除非文件仅附加,在这种情况下文件本身是同步机制) .
等待同步机制并不“糟糕” .
WaitHandle类型和派生类型提供了一种事件驱动机制,用于等待与操作系统的连接 . 例如,如果您有一个
Task<T> task
并且您通过访问task.Result
等待结果,则内部实现不会使用Thread.Sleep
之间的调用进行轮询 . 它使用WaitHandle
-derived类型进行等待和同步 .有时,基于轮询的方法是必要的,就像您在项目符号列表中提供的一些示例一样,但通常您可以使用事件驱动的方法 . 这不是
Thread.Sleep
是 always 不好 - 只是它是 very often misused .到 wait 很好 . 至 wait with polling 通常不是(*) . 如果有任何方法可以使用 event-driven wait ,您通常应该努力使用它 .
我对你究竟是什么感觉没有很好的感觉,所以我不会详细说明 . 如果您发表评论我可以扩展我的答案 .
(*)理论上的原因 waiting with polling 不好如下:
假设我的代码如下所示:
Begin()
开始一些操作 .Done()
返回true
表示操作已完成 . 假设这将在大约T
时间之后发生 . 然后:线程唤醒并检查条件(调用
Done()
)T/D
次从
START
到STOP
的持续时间包括预期的D/2
纯粹是因为Thread.Sleep
您应该选择
D
的值是多少?当您增加D
时,START
到STOP
的预期持续时间会线性增加 . 当你减少D
时,(绑定)迭代次数增加为1/D
. 这两个都很糟糕,找到正确的D
是有问题的 .现在将其与 event-driven wait 进行比较:
从理论上讲,只要
WaitDone()
不知何故神奇地等到操作完成但不再存在, waiting with polling 案例中发现的两个问题都消失了:这个线程等待了恰好合适的时间 - 不多也不少!重申我开始的观点:在.NET中,
WaitHandle
类和派生类型是促进这种方法的原因 .