简体   繁体   中英

How to stop a System.Threading.Timer and cancel any callback that is running?

Example of my code:

Timer timer = new Timer(Timer_Tick, null, Timeout.Infinite, Timeout.Infinite);
Mutex timerSync = new Mutex();

void StartWork()
{
    timer.Change(0, 1); //Start timer
    //Do something...
}

void Dispose()
{
    timer.Change(Timeout.Infinite, Timeout.Infinite); //Stop timer
    timer.Dispose();
    timerSync.Dispose();
    //Dispose other things...
}

void Timer_Tick()
{
    if (timerSync.WaitOne(0))
    {
        try
        {
            //Do something...
        }
        catch (Exception)
        {
            //If any exception occurs, abort the "Tick" callback!
            return;
        }
        finally
        {
            //Whatever happens, release the mutex!
            timerSync.ReleaseMutex();
        }
    }
}

When I stop the timer and dispose it, this not stops the current callback, that generates errors.
In particular, if a callback is in running, I get an ObjectDisposedException related to the mutex.
For the way the execution is structured, if I use that mutex on "Dispose" method, this causes a deadlock.
I have already thought of a solution: Use another try-catch block to handle exceptions related to the mutex.

But I want to know if there is a method to force to cancel any callback of the timer when it is disposed.

According to the documentation, you should use the Dispose(WaitHandle) overload:

Releases all resources used by the current instance of Timer and signals when the timer has been disposed of.

When this method completes, it signals the WaitHandle specified by the notifyObject parameter.Use this overload of the Dispose method if you want to be able to block until you are certain that the timer has been disposed. The timer is not disposed until all currently queued callbacks have completed.

void Dispose()
{
    timer.Change(Timeout.Infinite, Timeout.Infinite); //Stop timer

    var waiter = new ManualResetEvent(false);
    timer.Dispose(waiter);
    waiter.WaitOne();
    waiter.Dispose();

    timerSync.Dispose();
    //Dispose other things...
}

If you don't want to wait for the current callback to be executed at all, you could follow the IDisposable pattern:

Timer timer = new Timer(Timer_Tick, null, Timeout.Infinite, Timeout.Infinite);
Mutex timerSync = new Mutex();
private bool _disposed;

void Dispose()
{
     _disposed = true;
    ...
}

void Timer_Tick()
{
    if (_disposed)
    {
        return;
    }

    ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM