Interrupt a sleeping Thread

后端 未结 7 1068
暗喜
暗喜 2020-12-13 16:08

Is there a way to Interupt a sleeping thread? If I have code similar to this.

while(true){
    if(DateTime.Now.Subtract(_lastExecuteTime).TotalHours > 1)         


        
相关标签:
7条回答
  • 2020-12-13 16:15

    Instead of using Thread.Sleep use ManualResetEvent.WaitOne.

    while (true) {
        if(DateTime.Now.Subtract(_lastExecuteTime).TotalHours > 1) {
            DoWork();
            _lastExecuteTime = DateTime.Now();
            continue; 
        }
        if (terminate.WaitOne(10000)) {
            break;
        }
    }
    

    Where terminate is a ManualResetEvent1 that you can Set to request termination of the loop.

    Update:

    I just noticed that you said you are already using ManualResetEvent to terminate the background work (I am assuming that is in DoWork). Is there any reason why you cannot use the same MRE? If that is not possible there certainly should not be an issue using a different one.

    Update 2:

    Yeah, so instead of Thread.Sleep(5000) in ExecuteWorker do _shutdownEvent.WaitOne(5000) instead. It would look like the following.

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

    1There is also a ManualResetEventSlim class in .NET 4.0.

    0 讨论(0)
  • 2020-12-13 16:15

    Instead of using Thread.Sleep, you can use Monitor.Wait with a timeout - and then you can use Monitor.Pulse from a different thread to wake it up.

    Don't forget you'll need to lock on the monitor before calling either Wait or 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);
    }
    
    0 讨论(0)
  • 2020-12-13 16:15

    You can use Thead.Interrupt to wake a sleeping thread. It will cause a ThreadInteruptedException on the blocked thread, so it's not the most elegant or efficient approach.

    You also need to be weary that interrupting a thread in this way is unsafe. It does not provide control over the point at which the thread is aborted, and therefore should not be used without careful consideration as to the consequences. As mentioned in other answers already, it's is far better to use signal based techniques to control when the thread terminates in a controlled manner.

    0 讨论(0)
  • 2020-12-13 16:20

    Here are three ways to implement a cancelable Sleep method, using the Task.Delay+CancellationToken combination. The first one must handle an AggregateException in case of cancellation, so it is a bit verbose:

    public static void Sleep(int millisecondsTimeout, CancellationToken cancellationToken)
    {
        try
        {
            Task.Delay(millisecondsTimeout, cancellationToken).Wait();
        }
        catch (AggregateException aex) when (aex.InnerException is OperationCanceledException)
        {
            // Ignore exception
        }
    }
    

    The second handles an OperationCanceledException, so it is nicer:

    public static void Sleep(int millisecondsTimeout, CancellationToken cancellationToken)
    {
        try
        {
            Task.Delay(millisecondsTimeout).Wait(cancellationToken);
        }
        catch (OperationCanceledException)
        {
            // Ignore exception
        }
    }
    

    The third has no exception to catch, so it is the most succinct:

    public static void Sleep(int millisecondsTimeout, CancellationToken cancellationToken)
    {
        Task.WaitAny(Task.Delay(millisecondsTimeout, cancellationToken));
    }
    

    The Task.WaitAny accepts a params Task[] as argument, so it may be slightly less efficient than the previous options.

    More info about cancellation in general: Cancellation in Managed Threads

    0 讨论(0)
  • 2020-12-13 16:31

    If you want to DoWork() every hour, why not calculate the numer of ms required until the next hour is up and wait for that time? To cancel the sleep, you could either use a waitable synchro class, like the monitor as suggested by @Jon Skeet, or just set a flag in the thread that tells it to exit after the sleep instead of DoWork() and just forget about it - either the thread will kill itself when the time is up or your OS will kill it on app close, whichever comes first.

    Rgds, Martin

    0 讨论(0)
  • 2020-12-13 16:32

    Can't you just wrap your Thread.Sleep portion in a loop so that it loops 60 times then makes the other check? Of course all this does is trades off a simple if(somethingIndicatingQuit) versus the DateTIme if check, but assuming maybe you're real code does something more expensive, having a second loop could offer some minor CPU savings.

    0 讨论(0)
提交回复
热议问题