中断睡眠线程

有没有办法中断睡眠线程? 如果我有类似的代码。

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

其中terminateManualResetEvent 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将其唤醒。

不要忘记在调用WaitPulse之前你需要锁定显示器:

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