中断睡眠线程
有没有办法中断睡眠线程? 如果我有类似的代码。
while(true){ if(DateTime.Now.Subtract(_lastExecuteTime).TotalHours > 1){ DoWork(); _lastExecuteTime = DateTime.Now(); continue; } Thread.Sleep(10000) //Sleep 10 seconds if(somethingIndicatingQuit){ break; } }
我想每小时执行一次DoWork()。 所以,我想睡10秒钟。 每10分钟左右检查一次。 但是,如果将我的睡眠设置为10分钟,并且我想要终止此后台任务,我必须等待睡眠恢复。
我的实际代码是使用Threading.ManualResetEvent来关闭后台工作,但我的问题是使用ThreadSleep代码。 如有必要,我可以发布更多代码。
好的,我将在这里添加更完整的代码,因为我认为它将回答一些问题。
private readonly ManualResetEvent _shutdownEvent = new ManualResetEvent(false); private readonly ManualResetEvent _pauseEvent = new ManualResetEvent(true); private Thread _backGroundWorkerThread; //This starts our work public void Start() { _backGroundWorkerThread = new Thread(ExecuteWorker) {IsBackground = true, Name = WorkerName + "_Thread"}; _shutdownEvent.Reset(); _backGroundWorkerThread.Start(); } internal void Stop() { //Signal the shutdown event _shutdownEvent.Set(); //Make sure to resume any paused threads _pauseEvent.Set(); //Wait for the thread to exit _backGroundWorkerThread.Join(); } private void ExecuteWorker() { while (true) { _pauseEvent.WaitOne(Timeout.Infinite); //This kills our process if (_shutdownEvent.WaitOne(0)) { break; } if (!_worker.IsReadyToExecute) { //sleep 5 seconds before checking again. If we go any longer we keep our service from shutting down when it needs to. Thread.Sleep(5000); continue; } DoWork(); } }
我的问题在这里,
_backGroundWorkerThread.Join();
这等待在我的后台线程中运行的ExecuteWorker()中的Thread.Sleep。
而不是使用Thread.Sleep
使用ManualResetEvent.WaitOne
。
while (true) { if(DateTime.Now.Subtract(_lastExecuteTime).TotalHours > 1) { DoWork(); _lastExecuteTime = DateTime.Now(); continue; } if (terminate.WaitOne(10000)) { break; } }
其中terminate
是ManualResetEvent
1 ,您可以Set
为请求终止循环。
更新:
我刚刚注意到你说你已经在使用ManualResetEvent
来终止后台工作(我假设它在DoWork
)。 你有什么理由不能使用相同的MRE吗? 如果这是不可能的,那么使用不同的问题当然不应该是一个问题。
更新2:
是的,所以不要在ExecuteWorker
中使用Thread.Sleep(5000)
来执行_shutdownEvent.WaitOne(5000)
。 它看起来如下。
private void ExecuteWorker() { while (true) { _pauseEvent.WaitOne(Timeout.Infinite); //This kills our process if (_shutdownEvent.WaitOne(0)) { break; } if (!_worker.IsReadyToExecute) { //sleep 5 seconds before checking again. If we go any longer we keep our service from shutting down when it needs to. _shutdownEvent.WaitOne(5000); continue; } DoWork(); } }
1 .NET 4.0中还有一个ManualResetEventSlim
类。
您可以使用Monitor.Wait
超时 – 而不是使用Thread.Sleep
– 然后您可以使用来自不同线程的Monitor.Pulse
将其唤醒。
不要忘记在调用Wait
或Pulse
之前你需要锁定显示器:
// In the background thread lock (monitor) { // If we've already been told to quit, we don't want to sleep! if (somethingIndicatingQuit) { break; } Monitor.Wait(monitor, TimeSpan.FromSeconds(10)); if (somethingIndicatingQuit) { break; } } // To wake it up... lock (monitor) { somethingIndicatingQuit = true; Monitor.Pulse(monitor); }
您可以使用Thead.Interrupt唤醒睡眠线程。 它会在被阻塞的线程上导致ThreadInteruptedException
,因此它不是最优雅或最有效的方法。
你还需要感到厌倦,以这种方式中断线程是不安全的。 它不能控制线程中止的点,因此在不仔细考虑后果的情况下不应使用。 正如其他答案中已经提到的,使用基于信号的技术来控制线程何时以受控方式终止会好得多。
难道你不能将你的Thread.Sleep部分包装在一个循环中,以便它循环60次然后进行另一次检查吗? 当然,所有这些都是简单的if(somethingIndicatingQuit)与DateTIme进行交易(如果检查),但假设你真正的代码可以做更昂贵的事情,拥有第二个循环可以提供一些小的CPU节省。
如果你想每小时做一次DoWork(),为什么不在下一个小时结束之前计算所需的ms数并等待那个时间? 要取消睡眠,您可以使用等待的同步类,如@Jon Skeet建议的监视器,或者只是在线程中设置一个标志,告诉它在睡眠后退出而不是DoWork()而忘记它 – 当时间到了,线程会自动终止,或者你的操作系统会在应用程序关闭时杀死它,以先到者为准。
Rgds,马丁
为什么不使用BlockingCollection? 我认为它比接受的答案更优雅。 我也不认为与显示器相比有很多开销。
这是怎么做的:
将BlockingCollection初始化为成员:
BlockingCollection _sleeper = new BlockingCollection ();
绝对的
Thread.Sleep(10000)
做这个
int dummy; _sleeper.TryTake(out dummy, 10000);
唤醒它你可以使用这个代码
_sleeper.Add(0);