Сравните с использованием потока.Режим ожидания и таймер для отложенного выполнения

StackOverflow https://stackoverflow.com/questions/391621

Вопрос

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

Должен ли я использовать

Thread thread = new Thread(() => {
    Thread.Sleep(millisecond);
    action();
});
thread.IsBackground = true;
thread.Start();

Или

Timer timer = new Timer(o => action(), null, millisecond, -1);

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

Но для использования Timer у Timer есть метод dispose .Поскольку выполнение задерживается, я не знаю, как распорядиться таймером.У вас есть какие-нибудь предложения?

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

Это было полезно?

Решение

Одно из отличий заключается в том , что System.Threading.Timer отправляет обратный вызов в поток пула потоков, вместо того чтобы каждый раз создавать новый поток.Если вам нужно, чтобы это происходило более одного раза в течение срока службы вашего приложения, это сэкономит накладные расходы на создание и уничтожение множества потоков (процесс, который является очень ресурсоемким, как указано в статье, на которую вы ссылаетесь), поскольку он просто повторно использует потоки в пуле, и если у вас будет работать более одного таймера одновременно, это означает, что у вас будет одновременно выполняться меньше потоков (также экономя значительные ресурсы).

Другими словами, Timer это будет намного эффективнее.Это также может быть более точным, поскольку Thread.Sleep гарантированно будет ждать по крайней мере столько, сколько вы укажете (ОС может перевести его в спящий режим на гораздо больший срок).Предоставлено, Timer по-прежнему не будет точно, но цель состоит в том, чтобы запустить обратный вызов как можно ближе к указанному времени, тогда как это НЕ обязательно является целью Thread.Sleep.

Что касается уничтожения Timer, обратный вызов может принимать параметр, так что вы, возможно, сможете передать Timer себя в качестве параметра и вызывайте Dispose при обратном вызове (хотя я не пробовал этого - я предполагаю, что возможно, что Таймер может быть заблокирован во время обратного вызова).

Редактировать:Нет, я думаю, вы не можете этого сделать, так как вы должны указать параметр обратного вызова в Timer сам конструктор.

Может быть, что-то вроде этого?(Опять же, на самом деле я этого не пробовал)

class TimerState
{
    public Timer Timer;
}

... и для запуска таймера:

TimerState state = new TimerState();

lock (state)
{
    state.Timer = new Timer((callbackState) => {
        action();
        lock (callbackState) { callbackState.Timer.Dispose(); }
        }, state, millisecond, -1);
}

Блокировка должна предотвращать попытку обратного вызова таймера освободить таймер до Timer поле было задано.


Добавление:Как отметил комментатор, если action() делает что-то с пользовательским интерфейсом, затем используя System.Windows.Forms.Timer вероятно, это лучший выбор, так как он выполнит обратный вызов в потоке пользовательского интерфейса.Однако, если это не так, и все сводится к Thread.Sleep против. Threading.Timer, Threading.Timer это тот путь, по которому нужно идти.

Другие советы

использование ThreadPool.RegisterWaitForSingleObject вместо таймера:

//Wait 5 seconds then print out to console. 
//You can replace AutoResetEvent with a Semaphore or EventWaitHandle if you want to execute the command on those events and/or the timeout
System.Threading.ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(false), (state, bTimeout) => Console.WriteLine(state), "This is my state variable", TimeSpan.FromSeconds(5), true);

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

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

Я помню, как реализовывал решение, похожее на решение Эрика.Это, однако, рабочий вариант ;)

class OneTimer
    {
        // Created by Roy Feintuch 2009
        // Basically we wrap a timer object in order to send itself as a context in order to dispose it after the cb invocation finished. This solves the problem of timer being GCed because going out of context
        public static void DoOneTime(ThreadStart cb, TimeSpan dueTime)
        {
            var td = new TimerDisposer();
            var timer = new Timer(myTdToKill =>
            {
                try
                {
                    cb();
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(string.Format("[DoOneTime] Error occured while invoking delegate. {0}", ex), "[OneTimer]");
                }
                finally
                {
                    ((TimerDisposer)myTdToKill).InternalTimer.Dispose();
                }
            },
                        td, dueTime, TimeSpan.FromMilliseconds(-1));

            td.InternalTimer = timer;
        }
    }

    class TimerDisposer
    {
        public Timer InternalTimer { get; set; }
    }

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

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

@miniscalope Нет, не используйте ThreadPool.Зарегистрируйте WaitForSingleObject вместо timer, System.Многопоточность.Таймер поставит обратный вызов в очередь для выполнения в потоке пула потоков по истечении времени и не потребует дескриптора wait, wait for single object свяжет поток threadpool, ожидающий сигнала о событии или истечения тайм-аута до того, как поток вызовет обратный вызов.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top