我如何优雅地停止System.Threading.Timer?

时间:2022-10-13 20:38:37

I have a Windows Service implemented in C# that needs to do some work every so often. I've implemented this using a System.Threading.Timer with a callback method that is responsible for scheduling the next callback. I am having trouble gracefully stopping (i.e. disposing) the timer. Here's some simplified code you can run in a console app that illustrates my problem:

我有一个用C#实现的Windows服务,需要经常做一些工作。我使用System.Threading.Timer实现了这个,它带有一个回调方法,负责安排下一个回调。我无法正常停止(即处理)计时器。这是一些简化的代码,您可以在控制台应用程序中运行,以说明我的问题:

const int tickInterval = 1000; // one second

timer = new Timer( state => {
                       // simulate some work that takes ten seconds
                       Thread.Sleep( tickInterval * 10 );

                       // when the work is done, schedule the next callback in one second
                       timer.Change( tickInterval, Timeout.Infinite );
                   },
                   null,
                   tickInterval, // first callback in one second
                   Timeout.Infinite );

// simulate the Windows Service happily running for a while before the user tells it to stop
Thread.Sleep( tickInterval * 3 );

// try to gracefully dispose the timer while a callback is in progress
var waitHandle = new ManualResetEvent( false );
timer.Dispose( waitHandle );
waitHandle.WaitOne();

The problem is that I get an ObjectDisposedException from timer.Change on the callback thread while waitHandle.WaitOne is blocking. What am I doing wrong?

问题是,当waitHandle.WaitOne阻塞时,我从回调线程上的timer.Change得到一个ObjectDisposedException。我究竟做错了什么?

The documentation for the Dispose overload I'm using says:

我正在使用的Dispose重载文档说:

The timer is not disposed until all currently queued callbacks have completed.

在完成所有当前排队的回调之前,不会释放计时器。

Edit: It appears that this statement from the documentation may be incorrect. Can someone verify?

编辑:似乎文档中的此声明可能不正确。有人可以验证吗?

I know that I could work around the problem by adding some signaling between the callback and the disposal code as Henk Holterman suggested below, but I don't want to do this unless absolutely necessary.

我知道我可以通过在回调和处理代码之间添加一些信号来解决这个问题,正如Henk Holterman在下面提到的那样,但除非绝对必要,否则我不想这样做。

4 个解决方案

#1


11  

With this code

有了这段代码

 timer = new Timer( state => {
                   // simulate some work that takes ten seconds
                   Thread.Sleep( tickInterval * 10 );

                   // when the work is done, schedule the next callback in one second
                   timer.Change( tickInterval, Timeout.Infinite );
               },
               null,
               tickInterval, // first callback in one second
               Timeout.Infinite );

it is almost certain that you will Dispose the timer while it is sleeping.

几乎可以肯定的是,你会在睡觉时处理掉计时器。

You will have to safeguard the code after Sleep() to detect a Disposed timer. Since there is no IsDisposed property a quick and dirty static bool stopping = false; might do the trick.

您必须在Sleep()之后保护代码以检测Disposed计时器。由于没有IsDisposed属性,因此快速且脏的静态bool stop = false;可能会做的伎俩。

#2


0  

Possible solution for protecting the callback method from working on a disposed timer:

保护回调方法不能处理已处理的计时器的可能解决方案:

https://*.com/a/15902261/193178

#3


0  

As described in "Concurrent Programming on Windows":
Create a dummy class InvalidWaitHandle, inheriting from WaitHandle:

如“Windows上的并发编程”中所述:创建一个虚拟类InvalidWaitHandle,继承自WaitHandle:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Threading;

namespace MyNameSpace
{
    class InvalidWaitHandle : WaitHandle
    {

    }
}

Hence you can Dispose a System.Threading.Timer properly like this:

因此,您可以像这样正确配置System.Threading.Timer:

public static void DisposeTimer()
{
   MyTimer.Dispose(new InvalidWaitHandle());
   MyTimer = null;
}

#4


-4  

You do not need to dispose of the timer to stop it. You can call Timer.Stop() or set Timer.Enabled to false, either of which will stop the timer from running.

您不需要丢弃计时器来停止它。您可以调用Timer.Stop()或将Timer.Enabled设置为false,其中任何一个都将停止计时器运行。

#1


11  

With this code

有了这段代码

 timer = new Timer( state => {
                   // simulate some work that takes ten seconds
                   Thread.Sleep( tickInterval * 10 );

                   // when the work is done, schedule the next callback in one second
                   timer.Change( tickInterval, Timeout.Infinite );
               },
               null,
               tickInterval, // first callback in one second
               Timeout.Infinite );

it is almost certain that you will Dispose the timer while it is sleeping.

几乎可以肯定的是,你会在睡觉时处理掉计时器。

You will have to safeguard the code after Sleep() to detect a Disposed timer. Since there is no IsDisposed property a quick and dirty static bool stopping = false; might do the trick.

您必须在Sleep()之后保护代码以检测Disposed计时器。由于没有IsDisposed属性,因此快速且脏的静态bool stop = false;可能会做的伎俩。

#2


0  

Possible solution for protecting the callback method from working on a disposed timer:

保护回调方法不能处理已处理的计时器的可能解决方案:

https://*.com/a/15902261/193178

#3


0  

As described in "Concurrent Programming on Windows":
Create a dummy class InvalidWaitHandle, inheriting from WaitHandle:

如“Windows上的并发编程”中所述:创建一个虚拟类InvalidWaitHandle,继承自WaitHandle:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Threading;

namespace MyNameSpace
{
    class InvalidWaitHandle : WaitHandle
    {

    }
}

Hence you can Dispose a System.Threading.Timer properly like this:

因此,您可以像这样正确配置System.Threading.Timer:

public static void DisposeTimer()
{
   MyTimer.Dispose(new InvalidWaitHandle());
   MyTimer = null;
}

#4


-4  

You do not need to dispose of the timer to stop it. You can call Timer.Stop() or set Timer.Enabled to false, either of which will stop the timer from running.

您不需要丢弃计时器来停止它。您可以调用Timer.Stop()或将Timer.Enabled设置为false,其中任何一个都将停止计时器运行。