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

时间:2008-12-24 15:04:36

标签: c# multithreading timer sleep delayed-execution

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

我应该使用

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

或者

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

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

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

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

6 个答案:

答案 0 :(得分:42)

一个区别是System.Threading.Timer在线程池线程上调度回调,而不是每次都创建一个新线程。如果你需要在应用程序的生命周期中多次发生这种情况,这将节省创建和销毁一堆线程的开销(这是一个非常耗费资源的过程,正如你引用的文章指出的那样),因为它会只是重用池中的线程,如果你一次有多个计时器,这意味着你将同时运行更少的线程(同时节省大量资源)。

换句话说,Timer会更有效率。它也可能更准确,因为只要你指定的时间(操作系统可能让它长时间睡眠),Thread.Sleep只保证等待至少。当然,Timer仍然不会完全准确,但目的是尽可能接近指定时间触发回调,而这不一定是Thread.Sleep的意图。

至于销毁Timer,回调可以接受一个参数,因此您可以将Timer本身作为参数传递并在回调中调用Dispose(尽管我还没有尝试过)这个 - 我想在回调期间可能会锁定Timer。

编辑:不,我想你不能这样做,因为你必须在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就可以了。

答案 1 :(得分: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);

答案 2 :(得分:14)

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

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

答案 3 :(得分: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; }
    }

答案 4 :(得分:1)

我使用System.Timer的唯一优点是我在大多数时间看到它用于轮询服务的长时间延迟(小时,分钟),开发人员经常忘记启动事件之前

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

答案 5 :(得分:0)

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