Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何优雅地停止System.Threading.Timer?_C#_.net_Multithreading - Fatal编程技术网

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

C# 如何优雅地停止System.Threading.Timer?,c#,.net,multithreading,C#,.net,Multithreading,我有一个用C#实现的Windows服务,需要经常做一些工作。我已经使用System.Threading.Timer和一个负责安排下一次回调的回调方法实现了这一点。我无法正常停止(即处理)计时器。下面是一些可以在控制台应用程序中运行的简化代码,说明了我的问题: const int tickInterval = 1000; // one second timer = new Timer( state => { // simulate some w

我有一个用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();
问题是我在
waitHandle.WaitOne
阻塞时从
timer.Change
回调线程上获得了
ObjectDisposedException
。我做错了什么

对于我使用的
Dispose
重载,表示:

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

编辑:文档中的此语句可能不正确。有人可以验证吗?


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

您不需要处理计时器来停止它。您可以调用
Timer.Stop()
或将
Timer.Enabled
设置为
false
,这两者都将停止计时器运行。

使用此代码

 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 );
几乎可以肯定的是,您将在计时器睡眠时处理它


您必须在Sleep()之后保护代码以检测已释放的计时器。由于没有IsDisposed属性,因此快速脏
静态bool stopping=false可能会起作用

保护回调方法不使用已释放计时器的可能解决方案:

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

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
    {

    }
}
因此,您可以像这样正确地处理System.Threading.Timer:

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

为什么你不能让计时器每10秒自动运行一次?你为什么要手动重新安排时间?@Tudor:因为有时候工作需要更长的时间,我不希望多个回调在执行时重叠。为什么在服务停止时手动公开计时器?当AppDomain卸载所有内存时,仍然会重新声明。@DanielHilgarth:在我的实际实现中,我正在从
ServiceBase.OnStop
执行计时器处理。我希望在让服务关闭之前,确保计时器已释放,并且没有正在进行的回调。我还需要运行其他关闭代码,在我确定计时器完全消失之前,我不想执行此操作。我开始怀疑,作为构造函数的匿名委托,您是否遇到了与线程回调引用相关的微妙问题……如果您(用于测试)遇到了什么问题将其转换为命名函数…假设计时器在辅助CLR线程上触发..我相信这些是
System.Timers.timer
的成员,而不是我正在使用的
System.Threading.timer
。谢谢;我只是想我不需要做这样的事情,因为文档中说“在当前所有排队的回调完成之前,计时器不会被释放。”这句话是什么意思?这句话的意思是你应该是对的,但我在timer.Change()上看到了ObjectDisposed异常。想知道“排队回调”这个短语是否字面上意味着其他可能挂起但当前未执行的回调,例如,根据定义,当前正在执行的回调是不排队的?@DavidW:是的,可能。我现在只想通过从
timer.Change
中捕获并抑制
ObjectDisposedException
来解决这个问题。也许最终会有人从微软看到这个,并给我一个更好的答案。你似乎有一个可重复的情况下,矛盾的文档,考虑张贴它。