C# 比较使用 Thread.Sleep 和 Timer 延迟执行
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/391621/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Compare using Thread.Sleep and Timer for delayed execution
提问by Chaowlert Chaisrichalermpol
I have a method which should be delayed running for a specified amount of time.
我有一个方法应该延迟运行指定的时间。
Should I use
我应该使用
Thread thread = new Thread(() => {
Thread.Sleep(millisecond);
action();
});
thread.IsBackground = true;
thread.Start();
Or
或者
Timer timer = new Timer(o => action(), null, millisecond, -1);
I had read some articlesabout using Thread.Sleep
is bad design. But I don't really understand why.
我读过一些关于使用糟糕设计的文章Thread.Sleep
。但我真的不明白为什么。
But for using Timer, Timer has dispose method. Since the execution is delayed, I don't know how to dispose Timer. Do you have any suggestions?
但是对于使用 Timer,Timer 有 dispose 方法。由于执行延迟,我不知道如何处置Timer。你有什么建议吗?
Or if you have alternative codes for delayed execution are also appreciate.
或者,如果您有延迟执行的替代代码,也将不胜感激。
采纳答案by Eric Rosenberger
One difference is that System.Threading.Timer
dispatches the callback on a thread pool thread, rather than creating a new thread every time. If you need this to happen more than once during the life of your application, this will save the overhead of creating and destroying a bunch of threads (a process which is very resource intensive, as the article you reference points out), since it will just reuse threads in the pool, and if you will have more than one timer going at once it means you will have fewer threads running at once (also saving considerable resources).
一个区别是System.Threading.Timer
在线程池线程上调度回调,而不是每次都创建一个新线程。如果您需要在应用程序的生命周期中多次发生这种情况,这将节省创建和销毁一堆线程的开销(这是一个非常资源密集型的过程,正如您参考的文章所指出的那样),因为它会只需重用池中的线程,如果您将有多个计时器同时运行,则意味着您将有更少的线程同时运行(也节省了大量资源)。
In other words, Timer
is going to be much more efficient. It also may be more accurate, since Thread.Sleep
is only guaranteed to wait at LEAST as long as the amount of time you specify (the OS may put it to sleep for much longer). Granted, Timer
is still not going to be exactly accurate, but the intent is to fire the callback as close to the specified time as possible, whereas this is NOT necessarily the intent of Thread.Sleep
.
换句话说,Timer
效率会更高。它也可能更准确,因为Thread.Sleep
只能保证至少等待您指定的时间(操作系统可能会让它休眠更长时间)。当然,Timer
仍然不会完全准确,但目的是在尽可能接近指定时间的情况下触发回调,而这不一定是Thread.Sleep
.
As for destroying the Timer
, the callback can accept a parameter, so you may be able to pass the Timer
itself as the parameter and call Dispose in the callback (though I haven't tried this -- I guess it is possible that the Timer might be locked during the callback).
至于销毁Timer
,回调可以接受一个参数,因此您可以将其Timer
本身作为参数传递并在回调中调用 Dispose(尽管我还没有尝试过这个 - 我想 Timer 可能是在回调期间锁定)。
Edit: No, I guess you can't do this, since you have to specify the callback parameter in the Timer
constructor itself.
编辑:不,我想你不能这样做,因为你必须在Timer
构造函数本身中指定回调参数。
Maybe something like this? (Again, haven't actually tried it)
也许是这样的?(再次,没有真正尝试过)
class TimerState
{
public Timer Timer;
}
...and to start the timer:
...并启动计时器:
TimerState state = new TimerState();
lock (state)
{
state.Timer = new Timer((callbackState) => {
action();
lock (callbackState) { callbackState.Timer.Dispose(); }
}, state, millisecond, -1);
}
The locking should prevent the timer callback from trying to free the timer prior to the Timer
field having been set.
锁定应防止计时器回调Timer
在设置字段之前尝试释放计时器。
Addendum: As the commenter pointed out, if action()
does something with the UI, then using a System.Windows.Forms.Timer
is probably a better bet, since it will run the callback on the UI thread. However, if this is not the case, and it's down to Thread.Sleep
vs. Threading.Timer
, Threading.Timer
is the way to go.
附录:正如评论者所指出的,如果action()
对 UI 执行某些操作,那么使用 aSystem.Windows.Forms.Timer
可能是更好的选择,因为它将在 UI 线程上运行回调。但是,如果情况并非如此,并且归结为Thread.Sleep
vs. Threading.Timer
,Threading.Timer
则是要走的路。
回答by Shawn
I think Thread.Sleep is fine if you really want to pause the application for a specified amount of time. I think the reason people say it is a bad design is because in most situations people don't actually want the application to pause.
我认为 Thread.Sleep 很好,如果你真的想暂停应用程序指定的时间。我认为人们说这是一个糟糕的设计的原因是因为在大多数情况下人们实际上并不希望应用程序暂停。
For example, I was working on a pop3 client where the programmer was using Thread.Sleep(1000) to wait while the socket retrieved mail. In that situation it was better to hook up an event handler to the socket and continuing program execution after the socket had completed.
例如,我正在开发一个 pop3 客户端,其中程序员使用 Thread.Sleep(1000) 等待套接字检索邮件。在这种情况下,最好将事件处理程序连接到套接字并在套接字完成后继续执行程序。
回答by StingyHyman
The only beef that I have with the System.Timer is that most of the time I have seen it used for long delays (hours, minutes) in polling services and developers often forget to launch the event Beforethey start the timer. This means that if I start the app or service, I have to wait until the timer elapses (hours, minutes) before it actually executes.
我对 System.Timer 唯一的缺点是,大部分时间我都看到它用于轮询服务中的长时间延迟(小时、分钟),开发人员经常忘记在启动计时器之前启动事件。这意味着如果我启动应用程序或服务,我必须等到计时器过去(小时、分钟)后才能真正执行。
Sure, this is not a problem with the timer, but I think that its often used improperly by because its just too easy to misuse.
当然,这不是计时器的问题,但我认为它经常被不当使用,因为它太容易被误用。
回答by StingyHyman
use ThreadPool.RegisterWaitForSingleObject
instead of 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);
回答by Matt
@miniscalope No don't use ThreadPool.RegisterWaitForSingleObject instead of timer, System.Threading.Timer will queue a callback to be executed on a thread pool thread when the time has elapsed and doesn't require a wait handle, wait for single object will tie up a threadpool thread waiting for the event to be signalled or the timeout to expire before the thread calls the callback.
@miniscalope 不,不要使用 ThreadPool.RegisterWaitForSingleObject 而不是计时器,System.Threading.Timer 将在时间已过并且不需要等待句柄时将要在线程池线程上执行的回调排队,等待单个对象将在线程调用回调之前,占用一个线程池线程,等待事件发出信号或超时到期。
回答by Froyke
I remember implementing a solution similar to Eric's one. This is however a working one ;)
我记得实现了一个类似于 Eric 的解决方案。然而,这是一个有效的;)
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; }
}