中断睡眠线程

时间:2011-09-16 17:57:47

标签: c# multithreading

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

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。

7 个答案:

答案 0 :(得分:34)

您可以使用Thread.Sleep超时,而不是使用Monitor.Wait,然后您可以使用其他线程中的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);
}

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

terminateManualResetEvent 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