比较使用 Thread.Sleep 和 Timer 延迟执行

发布于 2024-07-10 01:30:51 字数 673 浏览 7 评论 0 原文

我有一个方法应该延迟运行指定的时间。

我应该使用

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

或者

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

我读过一些文章,介绍如何使用 Thread.Sleep 是糟糕的设计。 但我真的不明白为什么。

但是,对于使用TimerTimer有一个dispose方法。 由于执行被延迟,我不知道如何处理Timer。 你有什么建议吗?

或者,如果您有延迟代码执行的替代建议,我们也将不胜感激。

I have a method which should be delayed from 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 articles about how using Thread.Sleep is bad design. But I don't really understand why.

However, for using Timer, Timer has a dispose method. Since the execution is delayed, I don't know how to dispose the Timer. Do you have any suggestions?

Or, if you have an alternative suggestion for delaying code execution, that would also be appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

熊抱啵儿 2024-07-17 01:30:51

一个区别是 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() 对 UI 做了一些事情,那么使用 System.Windows.Forms.Timer 可能是一个更好的选择,因为它将在 UI 线程上运行回调。 但是,如果情况并非如此,并且取决于 Thread.SleepThreading.Timer,则 Threading.Timer 是去。

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).

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.

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).

Edit: No, I guess you can't do this, since you have to specify the callback parameter in the Timer constructor itself.

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.


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.

紫南 2024-07-17 01:30:51

@miniscalope 不,不要使用 ThreadPool.RegisterWaitForSingleObject 而不是计时器,System.Threading.Timer 将在时间过去时将回调排队在线程池线程上执行,并且不需要等待句柄,等待单个对象将在线程调用回调之前,绑定一个线程池线程,等待事件发出信号或超时到期。

@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.

蓝眼泪 2024-07-17 01:30:51

我对 System.Timer 的唯一不满是,大多数时候我发现它在轮询服务中使用了很长的延迟(小时、分钟),并且开发人员经常忘记在他们之前启动该事件启动计时器。 这意味着如果我启动应用程序或服务,我必须等到计时器过去(小时,分钟)才能真正执行。

当然,这不是计时器的问题,但我认为它经常被不当使用,因为它太容易被误用。

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 Before they 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.

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.

太阳哥哥 2024-07-17 01:30:51

我记得实施了一种与埃里克类似的解决方案。
然而,这是一个有效的方法;)

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; }
    }

I remember implementing a solution similar to Eric's one.
This is however a working one ;)

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; }
    }
╭⌒浅淡时光〆 2024-07-17 01:30:51

我认为如果您确实想将应用程序暂停指定的时间,Thread.Sleep 就可以了。 我认为人们之所以说这是一个糟糕的设计,是因为在大多数情况下,人们实际上并不希望应用程序暂停。

例如,我正在开发一个 pop3 客户端,其中程序员使用 Thread.Sleep(1000) 来等待套接字检索邮件。 在这种情况下,最好将事件处理程序连接到套接字,并在套接字完成后继续执行程序。

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.

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.

时光倒影 2024-07-17 01:30:51

使用线程池。 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);

use ThreadPool.RegisterWaitForSingleObject instead of timer:

//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);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文