了Thread.sleep VS Monitor.Wait VS RegisteredWaitHan

2019-07-30 07:44发布

(下面的项目都有不同的目标,但即时通讯有趣知道他们是如何“暂停”)

问题

Thread.sleep -它的系统上影响性能,它占用一个线程,其等待?

怎么样Monitor.Wait ? 正是在他们的“等待”的方式有什么区别? ?他们阻碍线程与他们的期待?

怎么样RegisteredWaitHandle ? 此方法接受时,等待句柄发出信号时执行的委托。 虽然它在等待, 它不会阻碍线程。

所以一些线程被暂停,可以通过委托被唤醒,而其他人只是等待? 旋?

有人可以让事情更清晰?

编辑

http://www.albahari.com/threading/part2.aspx

Answer 1:

双方Thread.SleepMonitor.Wait把线程的WaitSleepJoin状态 :

WaitSleepJoin:线程被阻塞。 这可能是调用线程::睡眠或主题::加入,请求锁的结果 - 例如,通过调用监控::输入或显示器::等待 - 或线程同步对象上等待,如ManualResetEvent的。

RegisteredWaitHandle是通过调用获得RegisterWaitForSingleObject并传递WaitHandle 。 通常这类使用阻塞机制的所有后代,因此调用Wait才会使线程WaitSleepJoin (如AutoResetEvent )。

下面是从MSDN另一句名言:

该RegisterWaitForSingleObject方法检查指定对象的WaitHandle的的当前状态。 如果对象的状态是unsignaled,该方法注册一个等待操作。 等待操作是由从线程池中的线程执行。 该委托被工人线程时该对象的状态变为发信号或超时间隔过去执行。

因此,在池中的线程并等待信号。



Answer 2:

关于ThreadPool.RegisterWaitForSingleObject ,这不会占用每注册(收集或以其他方式)的螺纹。 您可以轻松地测试:请在LINQPad以下脚本调用这个方法20000次:

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...
}

如果代码捆绑20000个线程5秒“等待”的时间,就不可能正常工作。

编辑 -响应:

“这就是一个证明。但还有一个单独的线程,其仅检查信号?线程池中的?”

这是一个实现细节。 是的,这可能与卸载回调到管理线程池中的单个线程的执行,尽管有没有这方面的保证。 等待句柄最终是由操作系统,这将最有可能触发回调,也管理。 它可能在它的内部实现使用一个线程(或少数线程)。 或中断,它可能无法阻止一个单独的线程。 它甚至可能会根据操作系统版本而异。 这是一个实现细节,没有真正意义给我们的。



Answer 3:

虽然这是事实RegisterWaitForSingleObject创建等待线程,而不是每次调用创建一个。

从MSDN :

当需要新的等待线程会自动创建

从Raymond Chen的博客 :

...而不是花费整个线程,它有成本的一个线程的接近(但不完全)1/64

因此,使用RegisterWaitForSingleObject通常最好是创建自己的等待线程。



Answer 4:

Thread.SleepRegisteredWaitHandle不同层次的工作。 让我来把它清除掉:

进程有多个线程,其同时执行(取决于OS调度器)。 如果一个线程调用Thread.SleepMonitor.Wait ,它不旋转-它被投入WaitSleepJoin状态,CPU是给其他线程。

现在,当你有很多同时工作项目,您可以使用一个线程池 - 它创建多个线程,并使用自己的工作项目的认识派遣到它的线程调用的机制。 在这个模型中,工作线程,线程池中调度员打电话做了一些工作,然后返回到池中。 如果一个工人线程调用阻塞操作-像Thread.SleepMonitor.Wait -这在线程被“五花大绑”,因为线程池调度员不能将其用于其他工作项。

我不熟悉的实际API,但我认为RegisteredWaitHandle会告诉线程池调度程序调用工作线程需要的时候-与你自己的线程是不是“捆绑”,并能继续工作或返回到线程池。



Answer 5:

ThreadPool.g RegisterWaitForSingleObject并在其本机实现最终调用函数QueueUserAPC 。 见转子源(sscli20 \ CLR \ SRC \ VM \ win32threadpool.cpp(1981))。 与等待的Thread.Sleep你的线程将不会付诸当您使用RegisterWaitForSingleObject停了下来。

相反,这个线程FIFO队列与用户模式回调是注册当线程处于可报警状态,这会被调用。 这意味着你可以继续工作,当你的线程被阻塞的操作系统将在注册的回调给你的线程做的机会做一些有意义的事情在等待时的工作。

EDIT1:

为了完成分析。 在这并调用RegisterWaitForSingleObject回调被称为线程,当它处于可报警状态的线程。 一旦发生这种情况的线程调用也将RegisterWaitForSingleObject执行一个CLR回调不注册是由一个线程池回调等待线程这是只有在那里等待回调信号处理另一个回调。 然后,这个线程池回调等待的线程将检查定期对信号的回调。

这个等待的线程并最后调用QueueUserWorkItem对线程池线程要执行的回调信号。



文章来源: Thread.sleep vs Monitor.Wait vs RegisteredWaitHandle?