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

Альтернативы Thread.Sleep()

Каждые N минут мы хотим выполнить список задач. Итак, мы создали исполнитель задачи с

do { DoWork(); }while(!stopRequested)

Теперь мы хотим иметь паузу между рабочими циклами. Кажется, все думают, что Thread.Sleep() - это дьявол. Я видел упоминание использования материалов Monitor/Event, но у нас нет кого-то, кто говорит нам делать работу. Мы просто хотим делать вещи каждые N минут, как часы.

Итак, есть ли альтернатива или я нашел правильное использование Thread.Sleep?

Кто-то в другом сообщении упоминает WaitHandle.WaitOne() как альтернативу, но вы не можете назвать это по нестационарному методу, по-видимому? Или, по крайней мере, я не могу, потому что получаю ошибку времени компиляции..

Ссылка на объект требуется для нестатического поля, метода или имущество 'System.Threading.WaitHandle.WaitOne(System.TimeSpan)'

4b9b3361

Ответ 1

Вы должны называть WaitOne на WaitHandle, конечно. Это метод экземпляра. В противном случае, как бы он знал, чего ждать?

Определенно лучше иметь что-то, на что вы можете реагировать, вместо сна, чтобы вы могли заметить отмену без ожидания минут без причины. Другой альтернативой WaitHandle является использование Monitor.Wait/Pulse.

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

Для обычных рабочих задач вы можете посмотреть на использование Timer (либо System.Threading.Timer или System.Timers.Timer), или, возможно, даже Quartz.NET.

Ответ 2

По моему мнению, Thread.Sleep() плохо, потому что он вытесняет ресурсы потока из кеша, поэтому их нужно снова загружать. Неважно, но это может усугубить проблемы с производительностью в ситуациях с высокой нагрузкой. И тогда есть тот факт, что время не точное, и что он эффективно не может ждать длительностей около 10 мс...

Я использую этот фрагмент:

new System.Threading.ManualResetEvent(false).WaitOne(1000);

Легко, как пирог, и все это подходит на одной линии. Создает новый обработчик событий, который никогда не будет установлен, а затем ждет полный период таймаута, который вы указываете как аргумент WaitOne().

Хотя для этого конкретного сценария таймер, вероятно, будет более подходящим подходом:

var workTimer = new System.Threading.Timer(
    (x) => DoWork(),
    null,
    1000, // initial wait period
    300000); // subsequent wait period

Затем вместо установки переменной отмены вы остановите таймер с помощью workTimer.Stop().


Edit

Поскольку люди все еще находят это полезным, я должен добавить, что .NET 4.5 представляет метод Task.Delay, который еще более краток и также поддерживает async:

Task.Delay(2000).Wait(); // Wait 2 seconds with blocking
await Task.Delay(2000); // Wait 2 seconds without blocking

Ответ 3

Thread.Sleep не дьявол - вы можете использовать его для сценария, подобного этому. Это просто не очень надежно для коротких периодов времени.

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

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

Ответ 4

Три варианта, которые я могу придумать с головы, заключаются в следующем:

но я уверен, что многие упомянут - Thread.Sleep() не так уж плохо.

Ответ 5

Вы можете использовать ManualResetEvent, который вы устанавливаете, когда он останавливается, а затем на нем WaitOne.

Ответ 6

Я бы использовал таймер ожидания, который сигнализирует AutoResetEvent. Ваш поток должен ждать этого объекта WaitHandle. Вот небольшое консольное приложение, демонстрирующее этот подход:

class Program {
    const int TimerPeriod = 5;
    static System.Threading.Timer timer;
    static AutoResetEvent ev;
    static void Main(string[] args) 
    {
        ThreadStart start = new ThreadStart(SomeThreadMethod);
        Thread thr = new Thread(start);
        thr.Name = "background";
        thr.IsBackground = true;
        ev = new AutoResetEvent(false);
        timer = new System.Threading.Timer(
            Timer_TimerCallback, ev, TimeSpan.FromSeconds(TimerPeriod), TimeSpan.Zero);
        thr.Start();
        Console.WriteLine(string.Format("Timer started at {0}", DateTime.Now));
        Console.ReadLine();
    }

    static void Timer_TimerCallback(object state) {
        AutoResetEvent ev =  state as AutoResetEvent;
        Console.WriteLine(string.Format
             ("Timer callback method is executed at {0}, Thread: ", 
             new object[] { DateTime.Now, Thread.CurrentThread.Name}));
        ev.Set();
    }

    static void SomeThreadMethod() {
        WaitHandle.WaitAll(new WaitHandle[] { ev });
        Console.WriteLine(string.Format("Thread is running at {0}", DateTime.Now));
    }
}

Ответ 7

Если все, что вы делаете, это что-то вроде:

while (!stop_working)
{
    DoWork();
    Thread.Sleep(FiveMinutes);
}

Тогда я бы предложил не использовать нить. Во-первых, нет никаких особых оснований для того, чтобы нанести системный накладной выделенный поток, который проводит большую часть своего времени. Во-вторых, если вы установите флаг stop_working через 30 секунд после того, как поток перестанет спать, вам придется подождать четыре с половиной минуты до того, как поток просыпается и заканчивается.

Я бы предложил, как и другие: использовать таймер:

System.Threading.Timer WorkTimer;

// Somewhere in your initialization:

WorkTimer = new System.Threading.Timer((state) =>
    {
        DoWork();
    }, null, TimeSpan.FromMinutes(5.0), TimeSpan.FromMinutes(5.0));

И выключить:

 WorkTimer.Dispose();

Ответ 8

Вы можете использовать System.Timers.Timer и выполнить работу в своем обработчике ошибок.

Ответ 9

Как сказали другие ответчики, таймеры могут хорошо работать для вас.

Если вам нужен собственный поток, я бы не использовал Thread.Sleep здесь, хотя бы потому, что, если вам нужно закрыть приложение, нет хорошего способа сказать ему выйти из сна. Раньше я использовал что-то подобное.

class IntervalWorker
{
    Thread workerThread;
    ManualResetEventSlim exitHandle = new ManualResetEventSlim();

    public IntervalWorker()
    {
        this.workerThread = new Thread(this.WorkerThread);
        this.workerThread.Priority = ThreadPriority.Lowest;
        this.workerThread.IsBackground = true;
    }

    public void Start()
    {
        this.workerThread.Start();
    }

    public void Stop()
    {
        this.exitHandle.Set();
        this.workerThread.Join();
    }

    private void WorkerThread()
    {
        int waitTimeMillis = 10000; // first work 10 seconds after startup.

        while (!exitHandle.Wait(waitTimeMillis))
        {
            DoWork();

            waitTimeMillis = 300000; // subsequent work at five minute intervals.
        }
    }
}

Ответ 10

Я использовал таймер в приложении WinForms, а консольное приложение запускалось периодически с помощью ScheduledTask на сервере. WinForms с таймером использовался в тех случаях, когда я хочу, чтобы он отображал уведомления, которые он запускал, и что он нашел (я установил их для имитации в systray). В ситуациях, когда я хочу получать уведомление только в том случае, если что-то не так на сервере, я разместил их на сервере в качестве консольных приложений и запускал их как запланированные задачи. Это, кажется, работает достаточно хорошо.

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

Ответ 11

       try {
            TimeUnit.MILLISECONDS.sleep(1000L);
        } catch (InterruptedException e) {
            // handle
        }