Thread.Sleep() 的替代方法

发布于 2024-10-27 00:54:54 字数 466 浏览 2 评论 0原文

每 N 分钟我们想要运行一次任务列表。因此,我们创建了一个任务执行器,

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

现在我们希望在工作周期之间有一个暂停。每个人似乎都认为 Thread.Sleep() 是魔鬼。我见过提到使用监视器/事件的东西,但我们没有其他人告诉我们去做工作。我们只想像发条一样每 N 分钟做一些事情。

那么是否有替代方案或者我是否找到了 Thread.Sleep 的有效用法?

另一篇文章中的有人提到 WaitHandle.WaitOne() 作为替代方案,但显然您不能从非静态方法调用它?或者至少我不能,因为我收到编译时错误..

需要对象引用 非静态字段、方法或 财产 'System.Threading.WaitHandle.WaitOne(System.TimeSpan)'

Every N minutes we want to run through a list of tasks. 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. 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.

So is there an alternative or have I found a valid use of Thread.Sleep?

Someone in another post mentioned WaitHandle.WaitOne() as an alternative but you can't call that from a non-static method apparently? 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)'

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

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

发布评论

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

评论(12

扛刀软妹 2024-11-03 00:54:55

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

You could use a System.Timers.Timer and perform the work in its elapse handler.

深海不蓝 2024-11-03 00:54:55
       try {
            TimeUnit.MILLISECONDS.sleep(1000L);
        } catch (InterruptedException e) {
            // handle
        }
       try {
            TimeUnit.MILLISECONDS.sleep(1000L);
        } catch (InterruptedException e) {
            // handle
        }
赤濁 2024-11-03 00:54:55

对于非异步上下文,请使用此超级方法:

public static bool SleepPlus(int millisecondsTimeout, CancellationToken cancellationToken = default)
    => SleepPlus(TimeSpan.FromMilliseconds(millisecondsTimeout), cancellationToken);

public static bool SleepPlus(this TimeSpan timeout, CancellationToken cancellationToken = default)
{ try { return !WaitHandle.WaitAll(new[] { cancellationToken.WaitHandle }, timeout); } catch { return false; } }

true 如果超时成功,false 如果取消或出错,可以使用 cancellationToken 实现兼容性或其他目的目的。

该方法存在以下问题:
- 手柄数量限制为 64 个。
- 不能在 STA 线程上使用。
- 显然会消耗资源。

注意:如果不使用cancellationToken,则省略它,简单,是等待句柄的实现的一部分!
完美的实现:

public static bool SleepPlus2(this TimeSpan timeout) 
{
    try
    {
        using (var syncLock = new ManualResetEvent(false))
            return !syncLock.WaitOne(timeout);
    }
    catch { return false; }
}

public static bool SleepPlus3(this TimeSpan timeout, CancellationToken cancellationToken = default) 
{
        bool result;
        try
        {
            using (var syncLock = new ManualResetEvent(false))
            using (var registration = cancellationToken.Register(() => syncLock.Set()))
            {
                result = !syncLock.WaitOne(timeout);
                registration.Unregister();
            }
        }
        catch (Exception ex) 
        { 
            result = false;
            Debug.WriteLine($"Could not Sleep for {timeout.TotalMilliseconds}ms. Exception: {ex.Message}");
        }
        return result;
}

该解决方案基于本主题,并可选地使用取消,抑制 WaitHandle.WaitAll 的所有限制。

Please, for non asynchronous context use this supermethod:

public static bool SleepPlus(int millisecondsTimeout, CancellationToken cancellationToken = default)
    => SleepPlus(TimeSpan.FromMilliseconds(millisecondsTimeout), cancellationToken);

public static bool SleepPlus(this TimeSpan timeout, CancellationToken cancellationToken = default)
{ try { return !WaitHandle.WaitAll(new[] { cancellationToken.WaitHandle }, timeout); } catch { return false; } }

true if timeout is succeeded, false if cancelled or error, can use cancellationToken for compatibility or another purposes.

This method has issues how:
-There is a 64 handle limit.
-It cannot be used on an STA thread.
-Obviously can consume resource.

NOTE: If you do not use cancellationToken, omit it, simple, is part of implementation to wait the handle!
A perfect implementation:

public static bool SleepPlus2(this TimeSpan timeout) 
{
    try
    {
        using (var syncLock = new ManualResetEvent(false))
            return !syncLock.WaitOne(timeout);
    }
    catch { return false; }
}

public static bool SleepPlus3(this TimeSpan timeout, CancellationToken cancellationToken = default) 
{
        bool result;
        try
        {
            using (var syncLock = new ManualResetEvent(false))
            using (var registration = cancellationToken.Register(() => syncLock.Set()))
            {
                result = !syncLock.WaitOne(timeout);
                registration.Unregister();
            }
        }
        catch (Exception ex) 
        { 
            result = false;
            Debug.WriteLine(
quot;Could not Sleep for {timeout.TotalMilliseconds}ms. Exception: {ex.Message}");
        }
        return result;
}

this solution is based in this topic and uses a cancellation optionally, suppress all limitations of WaitHandle.WaitAll.

北方的巷 2024-11-03 00:54:54

据我了解, Thread.Sleep() 不好,因为它强制线程的资源脱离缓存,因此之后必须再次加载它们。没什么大不了的,但它可能会加剧高负载情况下的性能问题。还有一个事实是,计时并不精确,而且它实际上无法等待大约 10 毫秒以下的持续时间......

我使用这个片段:

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

简单如馅饼,一切都适合一行。创建一个永远不会设置的新事件处理程序,然后等待完整的超时时间,您将其指定为 WaitOne() 的参数。

不过,对于这种特定场景,计时器可能是更合适的方法:

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

然后,您可以使用 workTimer.Stop() 停止计时器,而不是设置取消变量。


编辑

由于人们仍然发现这很有用,我应该补充一点,.NET 4.5 引入了 Task.Delay 方法,更加简洁,而且还支持异步:

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

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

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

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


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:

Task.Delay(2000).Wait(); // Wait 2 seconds with blocking
await Task.Delay(2000); // Wait 2 seconds without blocking
热血少△年 2024-11-03 00:54:54

当然,您必须在WaitHandle上调用WaitOne。这是一个实例方法。否则它怎么知道要等待什么?

最好是有一些你可以做出反应的东西,而不是睡觉,这样你就可以注意到取消,而无需无缘无故地等待几分钟。 WaitHandle 的另一种替代方法是使用 Monitor.Wait/Pulse

但是,如果您使用 .NET 4,我会研究任务并行库必须提供的功能...它的级别比其他选项稍高,并且通常是一个经过深思熟虑的库。

对于常规工作任务,您可能需要考虑使用 TimerSystem.Threading.TimerSystem.Timers.Timer),或者可能甚至Quartz.NET

You have to call WaitOne on a WaitHandle, certainly. 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.

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.

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.

不语却知心 2024-11-03 00:54:54

Thread.Sleep 并不是魔鬼 - 您可以将它用于这样的场景。只是在短时间内不太可靠。

使用 WaitHandle 是一个不错的选择 - 但您需要等待句柄的特定实例。然而,它不会单独做到这一点。

话虽这么说,大多数时候,像这样的操作更适合使用计时器。

Thread.Sleep isn't the devil - you could use it for a scenario like this. 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. It won't do this alone, however.

That being said, most of the time, operations like this are better suited towards using a Timer.

不即不离 2024-11-03 00:54:54

我能立即想到的三个选项是:

但我相信很多人都会提到 - Thread.Sleep() 并不是那么糟糕。

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.

黯淡〆 2024-11-03 00:54:54

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

You could use an ManualResetEvent that you set when it's time to stop, and then do a WaitOne on it.

没有心的人 2024-11-03 00:54:54

我将使用一个等待计时器来发出 AutoResetEvent 信号。您的线程应该等待此 WaitHandle 对象。这是一个小型控制台应用程序,展示了这种方法:

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

I would use a waiting timer which signals an AutoResetEvent. Your thread should wait for this WaitHandle object. 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));
    }
}
冷…雨湿花 2024-11-03 00:54:54

正如其他回答者所说,计时器可能很适合您。

如果您确实想要自己的线程,我不会在这里使用 Thread.Sleep,因为如果您需要关闭应用程序,则没有好方法告诉它退出睡眠状态。我以前用过类似的东西。

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

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. 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.
        }
    }
}
归途 2024-11-03 00:54:54

我在 WinForms 应用程序中使用了计时器,并在服务器上使用 ScheduledTask 定期启动控制台应用程序。当我希望它弹出它运行的通知和它发现的内容时,就使用了带有计时器的 WinForms(我已将它们设置为最小化到系统托盘)。对于我只想在服务器出现问题时收到通知的情况,我将它们作为控制台应用程序放在服务器上,并作为计划任务运行。这似乎运作得很好。

我想我尝试在现在使用计时器的应用程序中使用睡眠,但不喜欢结果。一方面,使用计时器,我可以非常轻松地调用最小化的应用程序,以便设置或更改前面的设置。如果应用程序处于睡眠状态,您将很难在其睡眠时重新获得访问权限。

I've used both a Timer in a WinForms application and a console app started periodically by a ScheduledTask on a server. 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). 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.

冰魂雪魄 2024-11-03 00:54:54

如果你的所有线程正在做的事情是这样的:

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

那么我建议根本不使用线程。首先,没有特别充分的理由来承担大部分时间都在休眠的专用线程的系统开销。其次,如果在线程停止休眠 30 秒后设置 stop_working 标志,则必须等待四分半钟才能唤醒并终止线程。

我建议像其他人一样:使用计时器:

System.Threading.Timer WorkTimer;

// Somewhere in your initialization:

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

并关闭:

 WorkTimer.Dispose();

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.

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