如何确保 Windows 服务计时器生成的线程在停止 Windows 服务后完成执行?

发布于 2024-12-06 06:58:26 字数 3820 浏览 1 评论 0原文

我正在使用 System.Timers.Timer 构建 Windows 服务。由计时器委托计算的任务可能需要几秒钟到几分钟的时间。我想确保当服务停止时,当前运行的所有委托线程在被处置之前完成。

下面是代码,但它并没有达到我的预期,因为如果 Windows 服务在运行时停止,当前正在运行的线程永远不会完成。

public abstract class AgentServiceBase : ServiceBase
{
    private System.ComponentModel.IContainer components = null;
    private System.Timers.Timer _Timer;
    private string _logPath;
    private const int MAXNUMBEROFTHREADS = 10;
    protected int interval = 25000;
    protected int numberOfAllowedThreads = 2;

    public AgentServiceBase()
    {
        this.InitializeComponent();
        this._logPath = (Path.GetDirectoryName(Assembly.GetAssembly(this.GetType()).CodeBase)).Substring(6).Replace("/", @"\");
    }

    protected override void OnStart(string[] args)
    {
        if (args.Length > 0)
        {
            int.TryParse(args[0], out interval);
        }

        if (args.Length > 1)
        {
            int.TryParse(args[1], out numberOfAllowedThreads);
            if (numberOfAllowedThreads > MAXNUMBEROFTHREADS)
            {
                numberOfAllowedThreads = MAXNUMBEROFTHREADS;
            }
            if (numberOfAllowedThreads == 1)
            {
                numberOfAllowedThreads = 2;
            }
        }

        ThreadPool.SetMaxThreads(numberOfAllowedThreads, numberOfAllowedThreads);

        this._Timer = new System.Timers.Timer();
        this._Timer.Elapsed += new ElapsedEventHandler(PollWrapper);
        this._Timer.Interval = this.interval;
        this._Timer.Enabled = true;

    }

    protected override void OnStop()
    {
        this._Timer.Enabled = false;

        Process currentProcess = Process.GetCurrentProcess();

        foreach (Thread t in currentProcess.Threads)
        {
            t.Join();
        }

    }
    /// <summary>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    private void InitializeComponent()
    {
        components = new System.ComponentModel.Container();
        this.ServiceName = "Agent Service - Johnhenry";

    }

    private void PollWrapper(object sender, ElapsedEventArgs e)
    {
        try
        {
            this.Poll(sender, e);
        }
        catch (Exception exception)
        {
            string message = this.GetType().FullName + "  - Windows Service Exception\n";
            message += exception.GetNestedExceptionInSingleStringOutput();

            FileHelper.Log(message, this._logPath, "exception", FileHelper.LogFileNameChangeFrequency.DAYLY);
        }
    }

    protected abstract void Poll(object sender, ElapsedEventArgs e);
}

非常感谢,

Giuseppe

更新

经过几次不同的尝试来计算当前进程自己的线程数,我最终找到了一个更简单的解决方案,该解决方案使用计时器已启动且仍在运行的线程计数器。基于此,我在主线程上调用 Sleep 并发出 RequestAdditionalTime 直到所有线程结束。 修改后的2个方法如下:

    protected override void OnStop()
    {
        this._Timer.Enabled = false;

        while (numberOfRunningThreads > 0)
        {
            this.RequestAdditionalTime(1000);
            Thread.Sleep(1000);
        }

    }



    private void PollWrapper(object sender, ElapsedEventArgs e)
    {
        numberOfRunningThreads++;

        try
        {
            this.Poll(sender, e);
        }
        catch (Exception exception)
        {
            string message = this.GetType().FullName + "  - Windows Service Exception\n";
            message += exception.GetNestedExceptionInSingleStringOutput();

            FileHelper.Log(message, this._logPath, "exception", FileHelper.LogFileNameChangeFrequency.DAYLY);
        }
        finally
        {
            numberOfRunningThreads--;
        }
    }

I'm building a Windows Service using System.Timers.Timer. The tasks computed by the Timer's delegate can take from several seconds to several minutes. I would like to make sure that, when the service is stopped, all delegated threads currently running complete before being disposed.

Here is the code, however it does not do what I expect, as currently running threads never complete if the Windows Service is stopped while they are running.

public abstract class AgentServiceBase : ServiceBase
{
    private System.ComponentModel.IContainer components = null;
    private System.Timers.Timer _Timer;
    private string _logPath;
    private const int MAXNUMBEROFTHREADS = 10;
    protected int interval = 25000;
    protected int numberOfAllowedThreads = 2;

    public AgentServiceBase()
    {
        this.InitializeComponent();
        this._logPath = (Path.GetDirectoryName(Assembly.GetAssembly(this.GetType()).CodeBase)).Substring(6).Replace("/", @"\");
    }

    protected override void OnStart(string[] args)
    {
        if (args.Length > 0)
        {
            int.TryParse(args[0], out interval);
        }

        if (args.Length > 1)
        {
            int.TryParse(args[1], out numberOfAllowedThreads);
            if (numberOfAllowedThreads > MAXNUMBEROFTHREADS)
            {
                numberOfAllowedThreads = MAXNUMBEROFTHREADS;
            }
            if (numberOfAllowedThreads == 1)
            {
                numberOfAllowedThreads = 2;
            }
        }

        ThreadPool.SetMaxThreads(numberOfAllowedThreads, numberOfAllowedThreads);

        this._Timer = new System.Timers.Timer();
        this._Timer.Elapsed += new ElapsedEventHandler(PollWrapper);
        this._Timer.Interval = this.interval;
        this._Timer.Enabled = true;

    }

    protected override void OnStop()
    {
        this._Timer.Enabled = false;

        Process currentProcess = Process.GetCurrentProcess();

        foreach (Thread t in currentProcess.Threads)
        {
            t.Join();
        }

    }
    /// <summary>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    private void InitializeComponent()
    {
        components = new System.ComponentModel.Container();
        this.ServiceName = "Agent Service - Johnhenry";

    }

    private void PollWrapper(object sender, ElapsedEventArgs e)
    {
        try
        {
            this.Poll(sender, e);
        }
        catch (Exception exception)
        {
            string message = this.GetType().FullName + "  - Windows Service Exception\n";
            message += exception.GetNestedExceptionInSingleStringOutput();

            FileHelper.Log(message, this._logPath, "exception", FileHelper.LogFileNameChangeFrequency.DAYLY);
        }
    }

    protected abstract void Poll(object sender, ElapsedEventArgs e);
}

Many thanks,

Giuseppe

UPDATE:

After few different attempts with counting the current process's own threads I eventually settled with a simpler solution which is using a counter of the threads the timer had initiated and are still running. Based on that I call the Sleep on the main thread and issue a RequestAdditionalTime until all threads have ended.
Following the revised 2 methods:

    protected override void OnStop()
    {
        this._Timer.Enabled = false;

        while (numberOfRunningThreads > 0)
        {
            this.RequestAdditionalTime(1000);
            Thread.Sleep(1000);
        }

    }



    private void PollWrapper(object sender, ElapsedEventArgs e)
    {
        numberOfRunningThreads++;

        try
        {
            this.Poll(sender, e);
        }
        catch (Exception exception)
        {
            string message = this.GetType().FullName + "  - Windows Service Exception\n";
            message += exception.GetNestedExceptionInSingleStringOutput();

            FileHelper.Log(message, this._logPath, "exception", FileHelper.LogFileNameChangeFrequency.DAYLY);
        }
        finally
        {
            numberOfRunningThreads--;
        }
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

说不完的你爱 2024-12-13 06:58:26

您可以通过调用 RequestAdditionalTime 只要您的线程在 OnStop 在循环内(在调用Join()之前和/或之后)。

但请注意,Windows 可能会变得不耐烦并决定终止您的 Windows 服务 - 例如在关闭期间...

有关详细信息,请参阅 MSDN 参考,网址为 http://msdn.microsoft.com/en-us/library/system.serviceprocess.servicebase.aspx

You can achieve that by calling RequestAdditionalTime as long as your threads haven't finished the work yet in your implementation of OnStop inside the loop (before and/or after the call to Join()).

BUT BEWARE that Windows can get impatient and decide to kill your Windows Service - for example during shutdown...

For more information see the MSDN reference at http://msdn.microsoft.com/en-us/library/system.serviceprocess.servicebase.aspx

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文