如何停止 System.Threading.Timer 并取消任何 运行 的回调?

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

我的代码示例:

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

当我停止计时器并处理它时,这不会停止当前回调,这会产生错误。
特别是,如果回调在 运行 中,我会收到与互斥体相关的 ObjectDisposedException。
对于执行的结构方式,如果我在 "Dispose" 方法上使用该互斥锁,这会导致死锁。
我已经想到了一个解决方案:使用另一个try-catch块来处理与互斥锁相关的异常。

但是我想知道是否有方法可以强制取消定时器的任何回调。

根据文档,您应该使用 Dispose(WaitHandle) 重载:

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

如果你根本不想等待当前回调被执行,你可以遵循 IDisposable 模式:

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

    ...
}