据我所知,可以使用Thread.Yield代替WaitOne和ManualResetEvent来进行线程信令。
虽然我没有在幕后看到解释WaitOne的确切行为的文档,但我假设它将线程置于等待状态,并告诉OS调度程序检查每次在队列中轮到该线程时是否设置了ManualResetEvent。如果未设置,调度程序将不执行上下文切换,而跳到另一个线程。如果设置,调度程序会将线程置于运行状态,以便在WaitOne开始执行后的代码。
另一方面,无论Thread.Yield的状态如何,使用ManualResetEvent都会导致上下文切换,然后再进行检查。
我的理解正确吗?是否有解释WaitOne内部工作原理的文档?
P.S:以下是两个版本的示例代码,供演示之用:
var signal = new ManualResetEvent(false);
new Thread(() =>
{
Console.WriteLine("Waiting for signal...");
signal.WaitOne();
signal.Dispose();
Console.WriteLine("Got signal!");
}).Start();
Thread.Sleep(2000);
signal.Set(); // "Open" the signalbool signal = false;
new Thread(() =>
{
Debug.WriteLine("Waiting for signal...");
while(signal == false)
{
Thread.Yield();
}
Debug.WriteLine("Got signal!");
}).Start();
Thread.Sleep(2000);
signal = true; ; // "Open" the signal发布于 2018-10-26 23:04:36
首先,汉斯的评论是完全正确的:你正在发明你自己的旋转等待,糟糕。别干那事!
尽管如此,您的问题并不是您是否应该重新实现WaitOne,而是WaitOne是如何由没有实现它的人实现的,因为它还没有编写。考虑这个问题是完全合理的,这些功能不是神奇的,而是由人类实现的,那么它们是如何做到的呢?
这是一个实现细节,我手头没有运行时的源代码;实际的实现是在一个名为WaitOneNative的本机函数中。不过,我可以给你一些想法。
首先,您正确地注意到,Thread.Yield是一个更原始的操作,因此它可以作为构建更高级别操作(如WaitOne )的策略的一部分。但实际上,它可能不会像你所描述的那样简单地使用,原因有几点:
Thread.Yield确实创建了一个屏障,但从代码中并不能百分之百地看出bool的读取没有被删除,或者写不能延迟。我们想要非常,非常肯定的是,bool写的东西被捡起来了,而引入障碍并没有破坏性能。Thread.Yield将控制切换到当前处理器上的任何就绪线程。如果当前处理器上没有就绪线程,会发生什么情况?也许好好想想吧。是什么使这些代码不能加热整个CPU?如果要写入的线程位于不同的处理器上,会发生什么情况?涉及线程饥饿等的所有可能的场景是什么?但是等等,情况会变得更糟。WaitOne是否需要解决更微妙的问题?
好的。CLR有它必须维护的不变量。您必须记住,( CLR )从根本上说是作为COM的扩展而发明的,并且底层的实现深深地嵌入到COM世界中。特别是,所有关于编组的规则仍然适用。WaitOne有效地使线程处于休眠状态,但这可能会导致封送处理程序出现问题。克里斯·布鲁姆( Chris )关于这一点的文章特别令人恐惧和私奔:
https://blogs.msdn.microsoft.com/cbrumme/2004/02/02/apartments-and-pumping-in-the-clr/
读一读,看看你是否能理解所有的内容。自2004年以来,我已经读过几十遍了,我曾经是一名专业的COM程序员,我大概能读到其中的80%。这是很复杂的事情,如果您不理解它,您就无法编写一个满足CLR需求的正确的WaitOne实现。
https://stackoverflow.com/questions/53011616
复制相似问题