В чем разница между Thread.Sleep (время ожидания) и ManualResetEvent.Wait (время ожидания)? - PullRequest
10 голосов
/ 08 июня 2010

И Thread.Sleep (тайм-аут), и resetEvent.Wait (тайм-аут) приводят к тому, что выполнение приостанавливается как минимум на timeout миллисекунд. Я знаю, что Thread.Sleep заставляет поток отказаться от оставшейся части своего временного интервала, что, возможно, приводит к тому, что сон длится намного дольше, чем требовалось. Есть ли у метода Wait (timeout) объекта ManualResetEvent такая же проблема?

Редактировать : я знаю, что основной смысл ManualResetEvent - сигнализировать из другого потока - сейчас меня интересует только случай метода Wait события с указанным тайм-аутом, и нет другие абоненты, устанавливающие событие. Я хочу знать, надежнее ли вовремя проснуться, чем Thread.Sleep

Ответы [ 6 ]

21 голосов
/ 08 июня 2010

Thread.Sleep(timeout) вызывает безусловное ожидание, прежде чем выполнение будет возобновлено. resetEvent.WaitOne(timeout) заставляет поток ждать, пока (1) не сработает событие или (2) не истечет время ожидания.

Смысл использования событий состоит в том, чтобы запускать их из другого потока, чтобы вы могли непосредственно контролировать, когда поток просыпается. Если вам это не нужно, вы не должны использовать объекты событий.

РЕДАКТИРОВАТЬ: С точки зрения времени, они оба одинаково надежны. Однако ваш комментарий о «пробуждении вовремя» меня беспокоит. Зачем вам нужен код, чтобы проснуться вовремя? Sleep и WaitOne на самом деле не разработаны с учетом точности.

Только если timeout меньше 50 мс или около того, и вам нужна надежность , вам следует искать альтернативные методы синхронизации. Эта статья выглядит довольно неплохо.

8 голосов
/ 08 июня 2010

Основное различие между Thread.Sleep и ManualResetEvent.WaitOne заключается в том, что вы можете сигнализировать потоку, ожидающему ManualResetEvent используя Set метод, заставляя поток проснуться раньше, чем время ожидания.

Если вы не дадите сигнал, я бы ожидал, что они будут вести себя очень похожим образом.

Из .NET Reflector Я вижу, что метод ManualResetEvent.WaitOne в конечном итоге приводит к вызову метода extern со следующей сигнатурой:

int WaitOneNative(SafeWaitHandle waitHandle,
                  uint millisecondsTimeout,
                  bool hasThreadAffinity,
                  bool exitContext);

Принимая во внимание, что Thread.Sleep вызывает этот метод extern:

void SleepInternal(int millisecondsTimeout);

К сожалению, у меня нет исходного кода для этих методов, поэтому я могу только догадываться. Я полагаю, что в обоих вызовах поток будет запланирован, пока он ожидает истечения времени ожидания, причем ни один из них не будет особенно точным, чем другой.

6 голосов
/ 11 июня 2010

Для задержек и периодических изданий я нашел Monitor.Wait - хороший выбор.

object timelock = new object();

lock (timelock) { Monitor.Wait(timelock, TimeSpan.FromMilliseconds(X.XX)); }

Это дает превосходный результат .... Джиттер на 1 мс или лучше, в зависимости от особенностей приложения.1005 * Как вы уже знаете, Thread.Sleep (X) ненадежен и не может быть отменен .... Я избегаю его как чума.

4 голосов
/ 08 июня 2010

Функция Sleep () долгое время не работала таким образом. Его точность определяется периодом мультимедийного таймера, который вы можете изменить с помощью P / Invoking timeBeginPeriod (). К сожалению, на моей машине у меня есть какая-то программа, которая устанавливает этот период в одну миллисекунду, с точностью до миллисекунды. Вот некоторый код, чтобы попробовать для себя:

using System;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;

class Program {
    static void Main(string[] args) {
        //timeBeginPeriod(1);
        var sw1 = Stopwatch.StartNew();
        for (int ix = 0; ix < 100; ++ix) Thread.Sleep(10);
        sw1.Stop();
        var sw2 = Stopwatch.StartNew();
        var mre = new ManualResetEvent(false);
        for (int ix = 0; ix < 100; ++ix) mre.WaitOne(10);
        sw1.Stop();
        Console.WriteLine("Sleep: {0}, Wait: {1}", sw1.ElapsedMilliseconds, sw2.ElapsedMilliseconds);
        Console.ReadLine();
        //timeEndPeriod(1);
    }
    [DllImport("winmm.dll")]
    private static extern int timeBeginPeriod(int period);
    [DllImport("winmm.dll")]
    private static extern int timeEndPeriod(int period);
}

Вывод на мою машину:

Сон: 999, Ожидание: 1003

с переменностью около 5 миллисекунд.

2 голосов
/ 08 июня 2010

Как уже упоминалось, разница заключается в том, что WaitOne может вернуться до времени ожидания, если получит сигнал.Sleep гарантированно ожидает время ожидания.

Thread.Sleep в вызовах отражателя:

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void SleepInternal(int millisecondsTimeout);

ManualResetEvent.Wait в вызовах отражателя:

private static extern int WaitOneNative(SafeWaitHandle waitHandle, uint millisecondsTimeout, bool hasThreadAffinity, bool exitContext);

Не уверенесли есть разница между ними, но я посмотрю, смогу ли я что-нибудь найти.

1 голос
/ 08 июня 2010

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

В одной теме вы бы сказали:

    mre.WaitOne(10000); // ten seconds
    Console.WriteLine("Woke up!");

В другом вы бы сказали:

    mre.Set(); // this causes `WaitOne` to return in the first thread

Без вызова Set в другом потоке первый поток будет фактически спать в течение 10 секунд.

...