(以下项目有不同的目标,但我很有趣,知道他们如何“PAUSEd”)
问题
Thread.sleep
- 它是否会影响系统的性能?是否会使线程与其等待相关?
Monitor.Wait
怎么样?他们“等待”的方式有什么不同?他们在等待中占用一个线程吗?
RegisteredWaitHandle
怎么样?此方法接受在等待时执行的委托
手柄发出信号。在等待的时候,它不会占用一个线程。
所以某些线程暂停并且可以被代表唤醒,而其他线程只是等待?旋转?
有人可以让事情更清楚吗?
修改
http://www.albahari.com/threading/part2.aspx
答案 0 :(得分:8)
Thread.Sleep
和Monitor.Wait
都将帖子放入the WaitSleepJoin
state:
WaitSleepJoin:线程被阻止。这可能是打电话的结果 Thread :: Sleep或Thread :: Join,请求锁定 - 例如,通过 调用Monitor :: Enter或Monitor :: Wait - 或等待线程 同步对象,如ManualResetEvent。
RegisteredWaitHandle
是通过致电RegisterWaitForSingleObject并传递WaitHandle
获得的。通常,此类的所有后代都使用阻塞机制,因此调用Wait
将再次将该线程放入WaitSleepJoin
(例如AutoResetEvent
)。
这是MSDN的另一个引用:
RegisterWaitForSingleObject方法检查当前的状态 指定对象的WaitHandle。如果对象的状态没有信号, 该方法注册一个等待操作。执行等待操作 来自线程池的线程。委托由工人执行 当对象的状态变为信号或超时时的线程 间隔过去了。
因此,池中的一个线程会等待信号。
答案 1 :(得分:6)
关于ThreadPool.RegisterWaitForSingleObject
,不会为每次注册(合并或其他)绑定一个帖子。您可以轻松地测试它:在LINQPad中运行以下脚本,该脚本调用该方法20,000次:
static ManualResetEvent _starter = new ManualResetEvent (false);
void Main()
{
var regs = Enumerable.Range (0, 20000)
.Select (_ => ThreadPool.RegisterWaitForSingleObject (_starter, Go, "Some Data", -1, true))
.ToArray();
Thread.Sleep (5000);
Console.WriteLine ("Signaling worker...");
_starter.Set();
Console.ReadLine();
foreach (var reg in regs) reg.Unregister (_starter);
}
public static void Go (object data, bool timedOut)
{
Console.WriteLine ("Started - " + data);
// Perform task...
}
如果该代码在5秒“等待”期间占用20,000个线程,则无法正常工作。
修改 - 响应:
“这是一个证明。但是仍然有一个线程可以检查 仅信号?在线程池?“
这是一个实现细节。是的,可以使用单个线程实现,该线程将回调卸载到托管线程池,尽管不能保证这一点。等待句柄最终由操作系统管理,这很可能也会触发回调。它可能在其内部实现中使用一个线程(或少量线程)。或者使用中断,它可能不会阻止单个线程。它甚至可能根据操作系统版本而有所不同。这是一个与我们无关的实施细节。
答案 2 :(得分:4)
虽然它是真的RegisterWaitForSingleObject
创建等待线程,但不是每次调用都会创建一个。
来自MSDN:
在需要时自动创建新的等待线程
来自Raymond Chen的blog:
...而不是花费整个线程,它花费更接近(但不完全)1/64的线程
因此,使用RegisterWaitForSingleObject
通常比创建自己的等待线程更好。
答案 3 :(得分:3)
ThreadPool.g RegisterWaitForSingleObject最终调用其本机实现 QueueUserAPC。参见转子源(sscli20 \ clr \ src \ vm \ win32threadpool.cpp(1981))。与Wait Thread.Sleep不同,当您使用RegisterWaitForSingleObject时,您的线程不会停止。
而是为此线程注册了具有用户模式回调的FIFO队列,当线程处于可警告状态时将调用该队列。这意味着你可以继续工作,当你的线程被阻止时,操作系统将处理已注册的回调,让你的线程有机会在等待时做一些有意义的事情。
<强> EDIT1:强>
完成分析。在调用RegisterWaitForSingleObject的线程上,当线程处于可警告状态时,将在线程上调用回调。一旦发生这种情况,调用RegisterWaitForSingleObject的线程将执行CLR回调,该回调会注册另一个回调,该回调由线程池回调等待线程处理,该线程仅在那里等待信号回调。然后,此线程池回调等待线程将定期检查已发出信号的回调。
此等待线程最终调用QueueUserWorkItem,以便在线程池线程上执行信号回调。
答案 4 :(得分:3)
Thread.Sleep
和RegisteredWaitHandle
在不同级别工作。让我试着澄清一下:
进程有多个线程,它们同时执行(取决于操作系统调度程序)。如果一个线程调用Thread.Sleep
或Monitor.Wait
,它就不会旋转 - 它被置于WaitSleepJoin状态,并且CPU被赋予其他线程。
现在,当您有许多同时工作项时,您使用线程池 - 一种创建多个线程的机制,并使用自己对工作项的理解来调度对其线程的调用。在此模型中,从线程池调度程序调用工作线程以执行某些工作,然后返回池。如果工作线程调用阻塞操作 - 如Thread.Sleep
或Monitor.Wait
- 此线程被“绑定”,因为线程池调度程序无法将其用于其他工作项。
我不熟悉实际的API,但我认为RegisteredWaitHandle
会告诉线程池调度程序在需要时调用工作线程 - 并且你自己的线程没有“捆绑”,并且可以继续工作或返回线程池。