首页 文章

如何在服务停止时停止多线程Windows服务中的工作线程

提问于
浏览
10

我有一个Windows服务,它使用 生产环境 者/消费者队列模型,多个工作线程处理队列中的任务 . 这些任务可以运行很长时间,如果不是几小时,则按很多分钟的顺序运行,并且不涉及循环 .

我的问题是关于处理服务停止以在这些工作线程上优雅地结束处理的最佳方法 . 我在另一个SO question中读到,使用thread.Abort()是设计不良的标志,但似乎服务OnStop()方法在服务终止之前只有有限的时间完成 . 我可以在catchA中为ThreadAbortException做足够的清理(没有不一致状态的危险)所以在工作线程上调用thread.Abort()对我来说似乎没问题 . 是吗?有哪些替代方案?

7 回答

  • 0

    使用ManualResetEvent检查事件是否已发出信号,请参阅Thread Synchronization (C# Programming Guide)中的示例

  • 6

    的确,应该避免 Abort . 最好给他们一些时间优雅地退出 - 然后也许在超时后, maybe 考虑中止他们 - 但最终,服务停止可以通过杀死进程来做同样的事情 .

    我会尝试在我的队列中发出一个信号"flush and exit" - 很像 Close 方法here,但是在完成时会有某种信号 .

    如果你诉诸 Abort - 考虑这个过程致命受伤 . 尽快杀死它 .

  • 1

    为“shutdown”创建一个任务类型,并为每个工作线程将其注入 生产环境 者/消费者队列一次 .

    然后使用Thread.Join(具有超时)以确保关闭已完成 .

  • 2

    使用.NET 4.0,您可以利用 System.Threading.Tasks 命名空间来利用 Task 对象 . 简而言之,您可以指定 CancellationToken 以更优雅地处理任务中的取消/中止,无论是长期还是短期运行 .

    有关MSDN的更多详细信息,请参阅here .

  • 0

    实际修改的问题实际上与线程无关,而与如何停止长时间运行有关 . 我个人总是使用APM进行冗长的流和通信活动,如大文件传输 . 每个回调都在IO完成池线程上运行,并快速完成,处理适度的块并安排下一次传递 . 只需在套接字对象上调用 Close() 即可取消挂起操作 . 这比DIY线程管理更便宜,更有效 .


    如前所述, Abort() 是不好的业力,应该避免 .

    下面的材料是在从问题中排除循环案例之前编写的 .

    当长时间运行的进程循环时,它们都应在其循环条件中包含一个退出标志,以便您可以发出信号以退出 .

    bool _run; //member of service class
    
    //in OnStart
    _run = true;
    
    //in a method on some other thread
    while ((yourLoopCondition) & _run) 
    { 
      //do stuff
      foreach (thing in things) 
      {
        //do more stuff
        if (!_run) break;
      }
    }
    if (!_run) CleanUp();
    
    //in OnStop
    _run = false;
    

    严格来说,你应该使用挥发物,但由于只有控制逻辑才能设置标志并不重要 . 从技术上讲,存在一种竞争条件,但这意味着你可能会再次出现这种情况 .

  • 0

    这是我用来停止Windows服务中的线程的代码(请注意,我直接使用Threads而不是使用线程池):

    // signal all threads to stop
    this.AllThreadsStopSignal.Set();
    
    if (logThreads.IsDebugEnabled)
        logThreads.Debug ("Stop workers");
    
    // remember the time of the signal
    DateTime signalTime = DateTime.Now;
    
    // create an array of workers to be stopped
    List<IServiceWorker> workersToBeStopped = new List<IServiceWorker> (workers);
    
    while (true)
    {
        // wait for some time
        Thread.Sleep (1000);
    
        // go through the list and see if any workers have stopped
        int i = 0;
        while (i < workersToBeStopped.Count)
        {
            IServiceWorker workerToBeStopped = workersToBeStopped [i];
    
            if (log.IsDebugEnabled)
                log.Debug (String.Format (System.Globalization.CultureInfo.InvariantCulture,
                    "Stopping worker '{0}'. Worker state={1}",
                    workerToBeStopped.WorkerDescription,
                    workerToBeStopped.WorkerState));
    
            bool stopped = workerToBeStopped.JoinThread (TimeSpan.Zero);
    
            // if stopped, remove it from the list
            if (stopped)
            {
                workersToBeStopped.RemoveAt (i);
                if (log.IsDebugEnabled)
                    log.Debug (String.Format (System.Globalization.CultureInfo.InvariantCulture,
                        "Worker '{0}' was stopped.", workerToBeStopped.WorkerDescription));
            }
            else
            {
                i++;
                if (log.IsDebugEnabled)
                    log.Debug (String.Format (System.Globalization.CultureInfo.InvariantCulture,
                        "Worker '{0}' could not be stopped, will try again later. Worker state={1}",
                        workerToBeStopped.WorkerDescription,
                        workerToBeStopped.WorkerState));
            }
        }
    
        // if all workers were stopped, exit from the loop
        if (workersToBeStopped.Count == 0)
            break;
    
        // check if the duration of stopping has exceeded maximum time
        DateTime nowTime = DateTime.Now;
        TimeSpan duration = nowTime - signalTime;
    
        if (duration > serviceCustomization.ThreadTerminationMaxDuration)
        {
            // execute forced abortion of all workers which have not stopped
            foreach (IServiceWorker worker in workersToBeStopped)
            {
                try
                {
                    log.Warn (String.Format (System.Globalization.CultureInfo.InvariantCulture,
                        "Aborting worker '{0}.", worker.WorkerDescription));
                    worker.Abort ();
                    log.Warn (String.Format (System.Globalization.CultureInfo.InvariantCulture,
                        "Worker '{0}' aborted.", worker.WorkerDescription));
                }
                catch (ThreadStateException ex)
                {
                    log.Warn (String.Format (System.Globalization.CultureInfo.InvariantCulture,
                        "Worker '{0}' could not be aborted.", worker.WorkerDescription), ex);
                }
            }
            break;
        }
    }
    
  • 5

    无论如何,如果你的进程正在关闭,我个人也不会在主线程中进行清理 . 无论如何 .

    另一种选择是将工作线程标记为background个线程 . 这样,它们会在进程关闭时自动关闭 . 您可以在退出之前使用AppDomain.ProcessExit事件进行清理 .

相关问题