C# 在应用程序/服务关闭/停止之前,等待计时器已过事件完成

C# 在应用程序/服务关闭/停止之前,等待计时器已过事件完成,c#,timer,C#,Timer,总结: 在Windows service&Console应用程序中,我正在调用一个公共库,该库包含一个计时器,该计时器定期触发一个操作,该操作大约需要30秒才能完成。这很好,但是 当调用服务停止或应用程序退出且计时器位于ElapsedEventHandler中时,我需要服务停止/应用程序退出等待事件处理程序完成 我通过调用timer stop方法时检查布尔InEvent属性来实现此功能 虽然这是功能性的,但问题是:这是做这件事的最佳方式吗?是否有其他方法可以更好地达到这一目的 另一个问题是,我需

总结: 在Windows service&Console应用程序中,我正在调用一个公共库,该库包含一个计时器,该计时器定期触发一个操作,该操作大约需要30秒才能完成。这很好,但是

当调用服务停止或应用程序退出且计时器位于ElapsedEventHandler中时,我需要服务停止/应用程序退出等待事件处理程序完成

我通过调用timer stop方法时检查布尔InEvent属性来实现此功能

虽然这是功能性的,但问题是:这是做这件事的最佳方式吗?是否有其他方法可以更好地达到这一目的

另一个问题是,我需要使用“服务未能响应停止请求”来避免服务停止请求失败

这是我的实现

public sealed class TimedProcess : IDisposable
{
    static TimedProcess singletonInstance;
    bool InEvent;
    Timer processTimer;

    private TimedProcess()
    {
    }

    public static TimedProcess Instance
    {
        get
        {
            if (singletonInstance == null)
            {
                singletonInstance = new TimedProcess();
            }

            return singletonInstance;
        }
    }

    public void Start(double interval)
    {
        this.processTimer = new Timer();
        this.processTimer.AutoReset = false;
        this.processTimer.Interval = interval;
        this.processTimer.Elapsed += new ElapsedEventHandler(this.processTimer_Elapsed);
        this.processTimer.Enabled = true;
    }

    public void Stop()
    {
        if (processTimer != null)
        {
            while (InEvent)
            {
            }

            processTimer.Stop();
        }
    }

    void processTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        try
        {
            InEvent = true;
            // Do something here that takes ~30 seconds
        }
        catch
        {
        }
        finally
        {
            InEvent = false;
            processTimer.Enabled = true;
        }
    }

    public void Dispose()
    {
        if (processTimer != null)
        {
            Stop();
            processTimer.Dispose();
        }
    }
}
这就是在service OnStart/console应用程序main中调用它的方式:

TimedProcess.Instance.Start(1000);
这是如何在service OnStop和application main(待定按键)中调用它的:

编辑

System.Timers.Timer
的每个回调都在
线程池中排队。请注意,
System.Timers.Timer
可能有一个竞争条件(您可以阅读更多有关它的内容)。
System.Threading.Timer
是一个稍微好一点的包装器,由于它的简单性,我更喜欢使用它

您还没有描述足够的细节来了解您的特定应用程序是否能够处理这种竞争条件,因此很难判断。但是给定您的代码,在调用
Stop()
之后,可能会有一个回调排队等待
processTimer\u expressed


对于服务超时问题--

一种方法是调用
ServiceController
方法
WaitForStatus
,并超时。我在过去做过这件事,效果相当不错,尽管我记得有一些边缘案例等待了很长时间


请参阅参考资料。这里描述了一个示例用法。

一个可能的替代方法似乎是不在计时器回调本身中执行实际工作,而只是将一个工作项从那里排队到踏板池中执行工作。然后,您可以继续并处理计时器-线程池上当前运行的任何内容都将保持运行,并且您的服务可以立即响应停止请求,但线程池项目(如果排队)仍将得到处理。

可能最简单和最可靠的方法是使用
监视器。创建主程序和计时器回调可以访问的对象:

private object _timerLock = new object();
您的主程序在关闭前尝试锁定:

// wait for timer process to stop
Monitor.Enter(_timerLock);
// do shutdown tasks here
您的计时器回调也会锁定它:

void processTimer_Elapsed(object sender, ElapsedEventArgs e)
{
    if (!Monitor.TryEnter(_timerLock))
    {
        // something has the lock. Probably shutting down.
        return;
    }
    try
    {
        // Do something here that takes ~30 seconds
    }
    finally
    {
        Monitor.Exit(_timerLock);
    }
}
主程序一旦获得锁,就不应该释放它

如果您希望主程序在一段时间后继续运行并关闭,无论它是否已获得锁,请使用
Monitor.TryEnter
。例如,这将等待15秒

bool gotLock = Monitor.TryEnter(_timerLock, TimeSpan.FromSeconds(15));
如果能够获得锁,则返回值为
true


顺便说一下,我强烈建议您使用
System.Threading.Timer
而不是
System.Timers.Timer
。后者挤压异常,最终可能隐藏bug。如果在您的
appeased
事件中发生异常,它将永远不会逃逸,这意味着您永远不会知道它。有关更多信息,请参阅my。

抱歉,我没有提到定时功能也是从控制台应用程序调用的。我已经编辑了原始问题,你把它倒过来了
System.Timers.Timer
System.Threading.Timer
包装为底层计时器对象。它只是在
System.Threading.Timer
上添加了一些特性。
System.Timers.Timer
添加了一个供消费者使用的同步对象。Jim看了一下Microsoft的这篇文章(第三条注释):关于在服务器应用程序中使用System.Threading.Timer的好处。当你完成的时候,你也会遇到异常。我如何在主线程上弹出一个对话框,告诉用户这个“后台进程”正在进行,当主线程能够关闭项目/应用程序时,对话框就会消失?@JakeSmith:你应该把它作为一个新问题发布。
bool gotLock = Monitor.TryEnter(_timerLock, TimeSpan.FromSeconds(15));