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

我应该使用

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

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

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

但是对于使用定时器,计时器已经处置方法。由于执行被拖延,我不知道如何处置计时器。你有什么建议吗?

或如果你有替代码,用于延迟执行,也赞赏。

有帮助吗?

解决方案

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

在换句话说,Timer将是更加高效。这也可能是更准确,因为Thread.Sleep只保证至少长达等待为您指定的时间(OS可以把它睡觉时间更久)的量。诚然,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是要走的路。

其他提示

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

我想了Thread.Sleep是好的,如果你真的想暂停指定的时间量的应用。我认为人们之所以说这是一个不好的设计是因为在大多数情况下,人们并不真正想要应用程序暂停。

例如,我正在其中编程器使用Thread.sleep代码(1000),等待而插座检索的电子邮件POP3客户端上。在这种情况下这是更好地挂钩的事件处理程序的插座,并继续执行程序的插座已经完成了。

记得执行类似于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; }
    }

这是我与System.Timer唯一的牛肉是,大部分我所看到的时候它用于查询服务和开发人员长时间的延迟(小时,分钟)常常忘了启动事件的他们开始计时。这意味着,如果我启动应用程序或服务,我必须等到定时器结束(小时,分钟),它实际上执行之前。

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

@miniscalope没有不使用而不是定时器ThreadPool.RegisterWaitForSingleObject,System.Threading.Timer将排队等待回调时,时间已经过去了一个线程池中的线程来执行,不需要等待句柄,等待单个对象会占用一个线程池线程等待要被通知的事件或过期的线程调用回调之前超时。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top