有没有办法让睡眠线程中断?如果我有类似的代码。
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。
答案 0 :(得分:34)
您可以使用Thread.Sleep
超时,而不是使用Monitor.Wait
,然后您可以使用其他线程中的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);
}
答案 1 :(得分:15)
而不是使用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
来终止后台工作(我假设它在Thread.Sleep(5000)
中)。你有什么理由不能使用相同的MRE吗?如果那是不可能的,那么使用不同的问题当然不应该是一个问题。
更新2:
是的,而不是ExecuteWorker
而不是_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();
}
}
。它看起来如下。
ManualResetEventSlim
1 .NET 4.0中还有一个{{1}}类。
答案 2 :(得分:2)
您可以使用Thead.Interrupt唤醒睡眠线程。它会在被阻塞的线程上导致ThreadInteruptedException
,因此它不是最优雅或最有效的方法。
您还需要感到厌倦,以这种方式中断线程是不安全的。它不能控制线程中止的点,因此不应在未仔细考虑后果的情况下使用。正如其他答案已经提到的那样,使用基于信号的技术控制线程何时以受控方式终止会好得多。
答案 3 :(得分:0)
你不能只将你的Thread.Sleep部分包裹在一个循环中,这样它就会循环60次然后再检查吗?当然,所有这些都是一个简单的 if(somethingIndicatingQuit)与DateTIme进行交易(如果检查),但假设您真正的代码可以做更昂贵的事情,拥有第二个循环可以提供一些小的CPU节省。
答案 4 :(得分:0)
如果你想每小时做一次DoWork(),为什么不计算所需的ms数,直到下一个小时结束并等待那个时间?要取消睡眠,您可以使用可等待的同步类,如@Jon Skeet建议的监视器,或者只是在线程中设置一个标志,告诉它在睡眠后退出而不是DoWork()而忘记它 - 当时间到了,线程会自动终止,或者你的操作系统会在app关闭时将其杀死,以先到者为准。
RGDS, 马丁
答案 5 :(得分:0)
为什么不使用BlockingCollection?我认为它比接受的答案更优雅。我也不认为与显示器相比有很多开销。
这是如何做到的:
将BlockingCollection初始化为成员:
BlockingCollection<int> _sleeper = new BlockingCollection<int>();
insted of
Thread.Sleep(10000)
这样做
int dummy;
_sleeper.TryTake(out dummy, 10000);
将其唤醒,您可以使用此代码
_sleeper.Add(0);
答案 6 :(得分:0)
以下三种方法可以使用Task.Delay
+ CancellationToken
组合来实现可取消的Sleep
方法。第一个必须在取消的情况下处理AggregateException
,所以有点冗长:
public static void Sleep(int millisecondsTimeout, CancellationToken cancellationToken)
{
try
{
Task.Delay(millisecondsTimeout, cancellationToken).Wait();
}
catch (AggregateException aex) when (aex.InnerException is OperationCanceledException)
{
// Ignore exception
}
}
第二个处理OperationCanceledException
,所以更好:
public static void Sleep(int millisecondsTimeout, CancellationToken cancellationToken)
{
try
{
Task.Delay(millisecondsTimeout).Wait(cancellationToken);
}
catch (OperationCanceledException)
{
// Ignore exception
}
}
第三个没有例外,所以它是最简洁的:
public static void Sleep(int millisecondsTimeout, CancellationToken cancellationToken)
{
Task.WaitAny(Task.Delay(millisecondsTimeout, cancellationToken));
}
Task.WaitAny
接受一个params Task[]
作为参数,因此它的效率可能比以前的选项稍低。
有关一般取消的更多信息:Cancellation in Managed Threads