【问题标题】:Windows Service OnStop() not called未调用 Windows 服务 OnStop()
【发布时间】:2014-05-08 23:44:48
【问题描述】:

我用 C# 编写了一个作为 Windows 服务运行的程序。应用程序启动并运行良好,但是当我使用管理控制台停止服务时,没有调用 OnStop 函数。

OnStart 方法为主程序启动一个后台线程,该后台线程启动另一个线程和一个 ThreadPool 为其工作。

OnStop 我设置了一个布尔标志,所有其他线程检查它们是否应该停止处理。然后线程应该全部结束,程序应该结束。

这是我的 OnStart 的代码

protected override void OnStart(string[] args)
    {
        base.OnStart(args);
        mainProgram.IsBackground = true;
        mainProgram.Start();
    }

该代码有效。下面是 OnStop 的代码,据我所知,它从未被调用过。

protected override void OnStop()
    {

        Log.LogMessage("Caught shutdown signal from the OS", "debug");
        base.OnStop();
        shutdown = true;
        mainProgram.Join(15000);
        if (mainProgram.IsAlive) mainProgram.Abort();
    }

该日志消息永远不会被写入日志文件。

任何帮助将不胜感激。我什至不知道从哪里开始寻找。谢谢。

编辑 我解决了锁定后台线程的问题。我还注释掉了该日志语句,所以我知道日志语句不会导致问题。

除了布尔标志之外,我还添加了一个 ManualResetEvent。 OnStop 现在看起来像这样:

protected override void OnStop()
    {
        System.Diagnostics.Debugger.Break();
        //Log.LogMessage("Caught shutdown signal from the OS", "debug");
        base.OnStop();
        shutdown = true;
        ShutdownX.Set();  //this is the ManualResetEvent
        mainProgram.Join(15000);
        if (mainProgram.IsAlive) mainProgram.Abort();
    }

应该停止代码的地方在 mainProgram.RunAgent() 函数中(这是它自己的线程) 而(!关闭) {

                SqlCommand DbCommand = dbConnection.CreateCommand();
                DbCommand.CommandText = "SELECT id, SourceID, CastingSN, Result FROM db_owner.queue";

                SqlDataReader DbReader = null;
                try
                {
                    DbReader = DbCommand.ExecuteReader();

                    while (DbReader.Read() && !shutdown)
                    {

                        long SourceID = DbReader.GetInt64(1);
                        string CastingSN = DbReader.GetString(2);
                        bool Result = DbReader.GetBoolean(3);

                        WaitCallback callback = new WaitCallback(oComm.RunAgent);
                        CommunicatorState commstate = new CommunicatorState(CastingSN, Result, SourceID);
                        ThreadPool.QueueUserWorkItem(callback, commstate);
                        callback = null;
                        commstate = null;

                    }
                    //Console.WriteLine("Finished Queueing Threads");
                }
                catch (SqlException Ex)
                {
                    Log.LogMessage("There was an error with a query run on the FlexNet Database.", "error");
                    Log.LogMessage(">> " + Ex.Message, "error");
                }
                finally
                {
                    if (DbReader != null) DbReader.Dispose();
                    DbCommand.Dispose();
                }
                ManualResetEvent[] handles = new ManualResetEvent[2] { eventX, ShutdownX };
                WaitHandle.WaitAny(handles);

                //eventX.WaitOne(Timeout.Infinite, true);
            }

认为这应该从数据库中读取,将它找到的所有线程排队,然后等待所有线程完成处理(eventX 重置事件)或 ShutdownX 事件。

一旦触发 ShutdownX 事件,外部循环不应继续,因为 shutdown bool 为真,然后线程将关闭其 SQL 连接并应终止。这一切都不会发生。有什么想法吗?

【问题讨论】:

  • 除了在日志文件中看不到消息,还会发生什么?进程是否被杀死?您是否在服务管理应用程序中收到错误?事件日志中写了什么?
  • 您的其他线程是否会锁定 Log.LogMessage 正在写入的文件?
  • 检查进程是否挂起。此外,在停止服务之前尝试使用附加的 WinDbg 运行,这样您可以检查任何异常,如果进程被终止而不是正常停止(尽管事件日志应该显示这一点的证据)。您的 Log.LogMessage 方法是否有机会在进程终止之前刷新其缓冲区?
  • 除了 CanStop 为 false 之外,这里的典型故障模式是您的 LogMessage() 方法抛出异常,阻止其余代码运行。在 Windows 事件日志中查找消息。
  • 我一直在玩WinDbg,我认为问题出在一些后台线程上。这些线程从数据库中读取值,然后通过 TCP 套接字将它们传输到记录它们的另一台机器。如果我在数据库为空的情况下启动服务,程序将正常退出。如果后台线程正在运行进行数据传输,程序将不会响应 OnStop 请求。因此,我将对该部分进行更多调试,看看它是否有帮助。

标签: c# windows service


【解决方案1】:

您正在使用ThreadPool。据报道,OnStop 从未被称为until all tasks in the thread pool complete。另见herehere。但是,我倾向于这不是主要或唯一原因,因为某些人seem to be using it with success

在我看来,您的问题目前表明 OnStop 从未被调用,但您已经看到它发出的日志消息。所以我假设OnStop 确实被调用了,但是一些线程没有注意到这一点。

请使用单个全局对象将获取或设置shutdown 的所有代码包装在lock 语句中。这不是为了原子性或互斥性。这是为了确保多处理器系统上的正确内存屏障。

【讨论】:

  • 据我了解,ThreadPool 任务是后台任务,因此不会阻止应用程序关闭,但根据您的消息来源,我错了。那我得想办法管理这些线程了。
  • @bluesky74656 - 也许你不必这样做;看看更新的答案。如果它对您有任何帮助,除了ServiceBaseThreadStart 之外,我从未亲眼见过纯C# 方法,通常与FileSystemMonitor 等特殊用途的处理程序结合使用。因此,如果我仍然相信引用的 ThreadPool 理论,那将是我的首选。 (ServiceBase 在处理启动或停止极其缓慢的服务时确实有一些怪癖。)
  • 比这要复杂一些。如果ThreadPool 运行,则我手动创建的主线程和另一个线程将正常关闭,并且服务停止。如果ThreadPool 正在运行,那么我无法在 OnStop 方法中运行任何语句。这让我觉得,尽管官方文档说所有 ThreadPool 线程都是后台线程,但它们导致 OnStop 在完成之前无法运行。所以我认为修复必须是手动管理我的线程,而不是使用 ThreadPool。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多