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

问题描述 投票:55回答:6

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

我应该用吗?

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

要么

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

我读过一些关于使用articlesThread.Sleep是糟糕的设计。但我真的不明白为什么。

但是对于使用Timer,Timer有配置方法。由于执行延迟,我不知道如何配置Timer。你有什么建议吗?

或者如果你有延迟执行的替代代码也很感激。

c# multithreading timer sleep delayed-execution
6个回答
42
投票

一个区别是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.TimerThreading.Timer是要走的路。


16
投票

使用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);

14
投票

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

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


2
投票

我记得实现类似于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; }
    }

1
投票

我使用System.Timer的唯一的好处是,我在大多数时间看到它用于轮询服务的长时间延迟(小时,分钟),开发人员经常忘记启动事件在他们启动计时器之前。这意味着如果我启动应用程序或服务,我必须等到计时器过去(小时,分钟)才能实际执行。

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


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.