简体   繁体   English

Thread.Sleep() 的替代方案

[英]Alternatives to Thread.Sleep()

Every N minutes we want to run through a list of tasks.每 N 分钟我们要运行一个任务列表。 So we've created a task executor with a所以我们创建了一个任务执行器

do { DoWork(); }while(!stopRequested)

Now we want to have a pause between work cycles.现在我们想要在工作周期之间暂停。 Everyone seems to think Thread.Sleep() is the devil.每个人似乎都认为 Thread.Sleep() 是魔鬼。 I've seen mention of using Monitor/Event stuff but we don't have someone else telling us to do work.我已经看到提到使用监视器/事件的东西,但我们没有其他人告诉我们做工作。 We just want to do stuff every N minutes like clockwork.我们只想每 N 分钟做一次像发条一样的事情。

So is there an alternative or have I found a valid use of Thread.Sleep?那么有没有替代方法或者我找到了有效使用 Thread.Sleep 的方法?

Someone in another post mentioned WaitHandle.WaitOne() as an alternative but you can't call that from a non-static method apparently?有人在另一篇文章中提到 WaitHandle.WaitOne() 作为替代方案,但显然您不能从非静态方法调用它? Or at least I can't because I get a compile time error of..或者至少我不能,因为我得到了一个编译时错误..

An object reference is required for the non-static field, method, or property 'System.Threading.WaitHandle.WaitOne(System.TimeSpan)'非静态字段、方法或属性“System.Threading.WaitHandle.WaitOne(System.TimeSpan)”需要对象引用

By my understanding, Thread.Sleep() is bad because it forces the thread's resources out of the cache, so they have to be loaded again afterwards.根据我的理解, Thread.Sleep() 很糟糕,因为它强制线程的资源从缓存中取出,因此之后必须再次加载它们。 Not a big deal, but it could aggravate performance issues in high-load situations.没什么大不了的,但它可能会在高负载情况下加剧性能问题。 And then there's the fact that the timing isn't precise, and that it effectively can't wait for durations under about 10ms...然后是时间不精确的事实,并且它实际上无法等待大约 10 毫秒以下的持续时间......

I use this snippet:我使用这个片段:

new System.Threading.ManualResetEvent(false).WaitOne(1000);

Easy as pie and it all fits on one line.像馅饼一样简单,而且都在一条线上。 Creates a new event handler that will never be set, and then waits the full timeout period, which you specify as the argument to WaitOne() .创建一个永远不会设置的新事件处理程序,然后等待完整的超时时间,您将其指定为WaitOne()的参数。

Although, for this specific scenario, a Timer would probably be a more appropriate approach:尽管对于这种特定情况,计时器可能是更合适的方法:

var workTimer = new System.Threading.Timer(
    (x) => DoWork(),
    null,
    1000, // initial wait period
    300000); // subsequent wait period

Then, instead of setting a cancel variable, you would stop the timer with workTimer.Stop() .然后,您可以使用workTimer.Stop()停止计时器,而不是设置取消变量。


Edit :编辑

Since people are still finding this useful, I should add that .NET 4.5 introduces the Task.Delay method, which is even more concise and also supports async:由于人们仍然发现这很有用,我应该补充一点,.NET 4.5 引入了Task.Delay方法,它更加简洁并且还支持异步:

Task.Delay(2000).Wait(); // Wait 2 seconds with blocking
await Task.Delay(2000); // Wait 2 seconds without blocking

You have to call WaitOne on a WaitHandle , certainly.当然,您必须WaitHandle调用WaitOne It's an instance method.这是一个实例方法。 Otherwise how would it know what to wait for?否则它怎么知道等待什么?

It's definitely better to have something you can react to instead of sleep, so that you can notice cancellation without waiting minutes for no reason.一些你可以做出反应的东西而不是睡觉肯定更好,这样你就可以注意到取消而无需无缘无故地等待几分钟。 Another alternative to WaitHandle is to use Monitor.Wait / Pulse . WaitHandle另一种替代方法是使用Monitor.Wait / Pulse

However, if you're using .NET 4 I'd look into what the Task Parallel Library has to offer... it's at a slightly higher level than the other options, and is generally a well thought out library.但是,如果您使用 .NET 4,我会研究 Task Parallel Library 必须提供的功能……它比其他选项的级别稍高,并且通常是一个经过深思熟虑的库。

For regular work tasks you might want to look at using a Timer (either System.Threading.Timer or System.Timers.Timer ) or possibly even Quartz.NET .对于常规工作任务,您可能需要考虑使用TimerSystem.Threading.TimerSystem.Timers.Timer )甚至Quartz.NET

Thread.Sleep isn't the devil - you could use it for a scenario like this. Thread.Sleep不是魔鬼 - 您可以将它用于这样的场景。 It's just not very reliable for short durations.它只是在短时间内不是很可靠。

Using a WaitHandle is a good option - but you need a specific instance of a wait handle.使用 WaitHandle 是一个不错的选择 - 但您需要一个特定的等待句柄实例。 It won't do this alone, however.然而,它不会单独做到这一点。

That being said, most of the time, operations like this are better suited towards using a Timer.话虽如此,大多数情况下,像这样的操作更适合使用计时器。

The three options that I can think of off the top of my head are :我能想到的三个选项是:

but I am sure as many will mention - Thread.Sleep() isn't all that bad.但我相信很多人都会提到 - Thread.Sleep()并不是那么糟糕。

您可以使用在需要停止时设置的 ManualResetEvent,然后对其执行 WaitOne。

I would use a waiting timer which signals an AutoResetEvent.我会使用一个等待计时器来发出 AutoResetEvent 信号。 Your thread should wait for this WaitHandle object.您的线程应该等待这个 WaitHandle 对象。 Here is a small console app showing this approach:这是一个显示这种方法的小型控制台应用程序:

class Program {
    const int TimerPeriod = 5;
    static System.Threading.Timer timer;
    static AutoResetEvent ev;
    static void Main(string[] args) 
    {
        ThreadStart start = new ThreadStart(SomeThreadMethod);
        Thread thr = new Thread(start);
        thr.Name = "background";
        thr.IsBackground = true;
        ev = new AutoResetEvent(false);
        timer = new System.Threading.Timer(
            Timer_TimerCallback, ev, TimeSpan.FromSeconds(TimerPeriod), TimeSpan.Zero);
        thr.Start();
        Console.WriteLine(string.Format("Timer started at {0}", DateTime.Now));
        Console.ReadLine();
    }

    static void Timer_TimerCallback(object state) {
        AutoResetEvent ev =  state as AutoResetEvent;
        Console.WriteLine(string.Format
             ("Timer's callback method is executed at {0}, Thread: ", 
             new object[] { DateTime.Now, Thread.CurrentThread.Name}));
        ev.Set();
    }

    static void SomeThreadMethod() {
        WaitHandle.WaitAll(new WaitHandle[] { ev });
        Console.WriteLine(string.Format("Thread is running at {0}", DateTime.Now));
    }
}

If all your thread is doing is something like:如果您的所有线程都在执行以下操作:

while (!stop_working)
{
    DoWork();
    Thread.Sleep(FiveMinutes);
}

Then I would suggest not using a thread at all.然后我建议根本不要使用线程。 First, there's no particularly good reason to incur the system overhead of a dedicated thread that spends most of its time sleeping.首先,没有什么特别好的理由会导致大部分时间都在休眠的专用线程的系统开销。 Secondly, if you set the stop_working flag 30 seconds after the thread stops sleeping, you'll have to wait four and a half minutes before the thread wakes up and terminates.其次,如果您在线程停止休眠后 30 秒设置stop_working标志,则您必须等待四分半钟才能唤醒和终止线程。

I'd suggest as others have: use a timer:我建议像其他人一样:使用计时器:

System.Threading.Timer WorkTimer;

// Somewhere in your initialization:

WorkTimer = new System.Threading.Timer((state) =>
    {
        DoWork();
    }, null, TimeSpan.FromMinutes(5.0), TimeSpan.FromMinutes(5.0));

And to shut down:并关闭:

 WorkTimer.Dispose();

您可以使用 System.Timers.Timer 并在其经过处理程序中执行工作。

As other answerers have said, Timers may work well for you.正如其他回答者所说,计时器可能适合您。

If you do want your own thread, I wouldn't use Thread.Sleep here, if only because if you need to shut down the application, there's no good way to tell it to exit the sleep.如果你确实想要你自己的线程,我不会在这里使用 Thread.Sleep,因为如果你需要关闭应用程序,没有好的方法告诉它退出睡眠。 I've used something like this before.我以前用过这样的东西。

class IntervalWorker
{
    Thread workerThread;
    ManualResetEventSlim exitHandle = new ManualResetEventSlim();

    public IntervalWorker()
    {
        this.workerThread = new Thread(this.WorkerThread);
        this.workerThread.Priority = ThreadPriority.Lowest;
        this.workerThread.IsBackground = true;
    }

    public void Start()
    {
        this.workerThread.Start();
    }

    public void Stop()
    {
        this.exitHandle.Set();
        this.workerThread.Join();
    }

    private void WorkerThread()
    {
        int waitTimeMillis = 10000; // first work 10 seconds after startup.

        while (!exitHandle.Wait(waitTimeMillis))
        {
            DoWork();

            waitTimeMillis = 300000; // subsequent work at five minute intervals.
        }
    }
}

I've used both a Timer in a WinForms application and a console app started periodically by a ScheduledTask on a server.我在 WinForms 应用程序和控制台应用程序中使用了计时器,该应用程序由服务器上的 ScheduledTask 定期启动。 The WinForms with a timer has been used in cases when I want it to pop up notifications that it ran and what it found (I've set these to mimize to the systray).带有计时器的 WinForms 已用于在我希望它弹出它运行的通知以及它发现的内容的情况下(我已将这些设置为将它们最小化到系统托盘)。 For situations where I only want to be notified if something goes wrong on a server, I've put them on the server as console apps and run them as Scheduled Tasks.对于我只想在服务器出现问题时收到通知的情况,我将它们作为控制台应用程序放在服务器上并作为计划任务运行。 That seems to work quite well.这似乎工作得很好。

I think I tried using Sleep in the apps where I now use Timers, and didn't like the result.我想我尝试在我现在使用计时器的应用程序中使用睡眠,但不喜欢结果。 For one thing, using a Timer I am able to call up the minimized app very easily in order to set or change settings on the front.一方面,使用计时器我可以很容易地调用最小化的应用程序,以便在前面设置或更改设置。 If the app is asleep, you have difficulty regaining access while it is slumbering.如果应用程序处于睡眠状态,您将很难在它处于睡眠状态时重新获得访问权限。

       try {
            TimeUnit.MILLISECONDS.sleep(1000L);
        } catch (InterruptedException e) {
            // handle
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM