Подтвердить что ты не робот

Thread.sleep vs Monitor.Wait vs RegisteredWaitHandle?

(следующие пункты имеют разные цели, но им интересно знать, как они "ПАУЗА" )

вопросы

Thread.sleep - Это влияет на производительность системы? Связано ли это с потоком с ожиданием?

как насчет Monitor.Wait? Какая разница в том, как они "ждут"? они связывают поток с их ожиданием?

как насчет RegisteredWaitHandle? Этот метод принимает делегата, который выполняется при ожидании сигнализация. Пока его ожидание, он не связывает поток.

поэтому какой-то поток приостанавливается и может быть разбужен делегатом, а другие просто ждут? вращение?

может кто-нибудь прояснить ситуацию?

изменить

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

enter image description here

4b9b3361

Ответ 1

Оба Thread.Sleep и Monitor.Wait помещают поток в состояние WaitSleepJoin:

WaitSleepJoin: поток заблокирован. Это может быть результатом вызова Thread:: Sleep или Thread:: Join, запрос блокировки - например, по вызов Monitor:: Enter или Monitor:: Wait - или ожидания в потоке объект синхронизации, такой как ManualResetEvent.

RegisteredWaitHandle получается путем вызова RegisterWaitForSingleObject и передачи WaitHandle. Как правило, все потомки этого класса используют блокирующие механизмы, поэтому вызов Wait снова помещает поток в WaitSleepJoin (например, AutoResetEvent).

Здесь другая цитата из MSDN:

Метод RegisterWaitForSingleObject проверяет текущее состояние указанный объект WaitHandle. Если состояние объекта не установлено, метод регистрирует операцию ожидания. Выполняется операция ожидания по потоку из пула потоков. Делегат исполняется работником поток, когда состояние объекта становится сигналом или тайм-аут интервал.

Итак, поток в пуле ожидает для сигнала.

Ответ 2

Что касается ThreadPool.RegisterWaitForSingleObject, то не связывает поток за регистрацию (пул или иначе). Вы можете проверить это легко: запустите следующий script в 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...
}

Если этот код связывает 20 000 потоков в течение 5-секундного "ожидания", он не может работать.

Изменить - в ответ на:

"это доказательство, но есть ли еще один поток, который проверяет только сигналы? в пуле потоков?"

Это деталь реализации. Да, он может быть реализован с помощью одного потока, который разгружает обратные вызовы в пул управляемых потоков, хотя нет никаких гарантий этого. В конце концов, управление ожиданиями управляется операционной системой, которая, скорее всего, также вызовет обратные вызовы. Он может использовать один поток (или небольшое количество потоков) во внутренней реализации. Или с прерываниями, это может не блокировать один поток. Это может даже измениться в зависимости от версии операционной системы. Это деталь реализации, которая не имеет никакого отношения к нам.

Ответ 3

ThreadPool.g RegisterWaitForSingleObject действительно вызывает свою собственную реализацию QueueUserAPC. См. Источники ротора (sscli20\clr\src\vm\win32threadpool.cpp(1981)). В отличие от Wait Thread.Sleep, ваш поток не будет остановлен, если вы используете RegisterWaitForSingleObject.

Вместо этого для этого потока регистрируется очередь FIFO с обратными вызовами пользовательского режима, которые будут вызываться, когда поток находится в аварийном состоянии. Это означает, что вы можете продолжать работать, и когда ваш поток заблокирован, ОС будет работать над зарегистрированными обратными вызовами, давая вашему потоку возможность сделать что-то значимое, пока оно ждет.

Edit1:

Чтобы завершить анализ. В потоке, который вызывал RegisterWaitForSingleObject, обратный вызов вызывается в потоке, когда он находится в аварийном состоянии. Как только это произойдет, поток, который вызвал RegisterWaitForSingleObject, выполнит обратный вызов CLR, который регистрирует другой обратный вызов, который обрабатывается потоком ожидания обратного вызова пула потоков, который существует только для ожидания сигнальных обратных вызовов. Этот поток ожидания обратного вызова потока потока будет проверяться регулярными интервалами для сигнальных обратных вызовов.

Этот поток ожидания, наконец, вызывает QueueUserWorkItem для сигнального обратного вызова, который должен выполняться в потоке пула потоков.

Ответ 4

Thread.Sleep и RegisteredWaitHandle работают на разных уровнях. Позвольте мне попробовать и очистить его:

Процессы имеют несколько потоков, которые выполняются одновременно (в зависимости от планировщика ОС). Если поток вызывает Thread.Sleep или Monitor.Wait, он не вращается - он помещается в состояние WaitSleepJoin, а ЦПУ передается другим потокам.

Теперь, когда у вас много одновременных рабочих элементов, вы используете пул потоков - механизм, который создает несколько потоков и использует собственное понимание рабочих элементов для отправки вызовов в свои потоки. В этих моделях рабочие потоки вызываются из диспетчера пула потоков, чтобы выполнить некоторую работу, а затем возвращаются обратно в пул. Если рабочий поток вызывает операцию блокировки - например, Thread.Sleep или Monitor.Wait - этот поток "привязан", так как диспетчер пула потоков не может использовать его для дополнительных рабочих элементов.

Я не знаком с фактическим API, но я думаю, что RegisteredWaitHandle сообщит диспетчеру пула потоков для вызова рабочего потока при необходимости - и ваш собственный поток не "привязан" и может продолжить свою работу или верните пул потоков.

Ответ 5

Пока он true RegisterWaitForSingleObject создает потоки ожидания, не каждый вызов создает его.

Из MSDN:

Новые потоки ожидания создаются автоматически при необходимости

Из Раймонда Чена blog:

... вместо того, чтобы стоить целую цепочку, он стоит что-то ближе (но не точно) 1/64 от потока

Таким образом, использование RegisterWaitForSingleObject обычно предпочтительнее создавать собственные потоки ожидания.