Сравните использование потока.Sleep и таймер для отложенного выполнения
у меня есть метод,который должен быть отложен на определенное время.
должен ли я использовать
Thread thread = new Thread(() => {
Thread.Sleep(millisecond);
action();
});
thread.IsBackground = true;
thread.Start();
или
Timer timer = new Timer(o => action(), null, millisecond, -1);
Я читал некоторые статьи о Thread.Sleep
- это плохой дизайн. Но я не совсем понимаю почему.
но для использования таймера таймер имеет метод dispose. Поскольку выполнение задерживается, я не знаю, как распорядиться таймером. У вас есть предложения?
или если у вас есть альтернатива коды для отложенного исполнения также ценятся.
6 ответов
одно отличие - это система.Нарезка резьбы.Таймер отправляет обратный вызов в поток пула потоков, а не создает новый поток каждый раз. Если вам нужно, чтобы это произошло более одного раза в течение срока службы вашего приложения, это сэкономит накладные расходы на создание и уничтожение группы потоков (процесс, который очень ресурсоемкий, как указывает статья, на которую вы ссылаетесь), так как он будет просто повторно использовать потоки в пуле, и если у вас будет более одного таймера, идущего одновременно, он будет означает, что у вас будет меньше потоков, работающих одновременно (также экономя значительные ресурсы).
другими словами, таймер будет гораздо более эффективным. Это также может быть более точным, так как поток.Сон гарантированно будет ждать по крайней мере столько времени, сколько вы укажете (ОС может заставить его спать намного дольше). Конечно, таймер по-прежнему не будет точно точным, но намерение состоит в том, чтобы запустить обратный вызов как можно ближе к указанному времени, тогда как это не так обязательно намерение нити.Спать.
что касается уничтожения таймера, обратный вызов может принять параметр, поэтому вы можете передать сам таймер в качестве параметра и вызвать Dispose в обратном вызове (хотя я не пробовал это-я думаю, что возможно, что таймер может быть заблокирован во время обратного вызова).
Edit: Нет, я думаю, вы не можете этого сделать, так как вы должны указать параметр обратного вызова в самом конструкторе таймера.
может быть что-то вроде это? (Опять же, на самом деле не пробовал)
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);
}
блокировка должна предотвратить обратный вызов таймера от попытки освободить таймер до того, как поле таймера было установлено.
Addendum: как отметил комментатор, если action() что-то делает с пользовательским интерфейсом, а затем использует систему.Окна.Формы.Таймер, вероятно, лучше, так как он будет запускать обратный вызов в потоке пользовательского интерфейса. Однако, если это не так, и все сводится к нитям.Сон и резьбы.Таймер, Резьба.Таймер-это путь.
использовать 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);
Я думаю, что нить.Сон в порядке, если вы действительно хотите приостановить приложение на определенное количество времени. Я думаю, что люди говорят, что это плохой дизайн, потому что в большинстве ситуаций люди на самом деле не хотят, чтобы приложение приостановилось.
например, я работал над клиентом pop3, где программист использовал поток.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.RegisterWaitForSingleObject вместо таймера, системы.Нарезка резьбы.Таймер будет стоять в очереди обратного вызова, который будет выполняться в потоке пула потоков, когда время истекло и не требует дескриптора ожидания, подождите, пока один объект свяжет поток threadpool, ожидая, пока событие будет сигнализировано или тайм-аут истечет, прежде чем поток вызовет обратный вызов.