如何安全地停止在Windows服务中运行的C#.NET线程?

我正在维护一些看起来像这样的代码。 这是一项Windows服务,每隔30分钟就能完成一些工作。 ActualWorkDoneHere方法运行大约需要30秒,但如果在运行时它停止,则会使事情处于错误状态。 防止这种情况发生的最佳方法是什么? 我应该用onstop方法中设置为false的布尔值替换While(true)(删除线程Abort调用)? 有没有办法判断线程是否在睡觉?

namespace WorkService { public partial class WorkService : ServiceBase { private Thread _workerThread = null; public WorkService() { InitializeComponent(); } protected override void OnStart(string[] args) { _workerThread = new Thread(new ThreadStart(DoWork)); _workerThread.Start(); } protected override void OnStop() { _workerThread.Abort(); } static void DoWork() { int sleepMinutes = 30; while (true) { ActualWorkDoneHere(); System.Threading.Thread.Sleep(new TimeSpan(0, sleepMinutes, 0)); } } } } 

当我有这样的东西时,我通常使用ManualResetEvent 。 这是在Stop()调用中设置的。 然后我等待超时:

 for (;;) { if (_stop.WaitOne(timeout)) break; DoSomething(); } 

自己实施是唯一安全的选择。 即使你找到一种方法来确定一个线程是否正在hibernate,如果你试图杀死它,你仍然会有竞争条件(因为它可能会在你检查之后和你杀死它之前开始处理)。

而不是Thread.Sleep你可以睡觉500毫秒并检查中止标志是否仍然是假,在30分钟之前再睡500毫秒等等,然后完成工作等等(这将是一个实用的方法)。 如果你想要更优雅的东西,你可以使用带有超时的ManualResetEvent来等待主线程发出信号表明其中止的时间。

哇大家都这么复杂。 使用计时器:

在比赛中:原来的post在OnStop上有一个已经修复的比赛。 据我所知,将服务置于停止状态不会中止用于服务定时器的线程池线程。 定时器触发和同时停止服务的条件无关紧要。 ActualWorkDoneHere()将运行或不运行。 两者都是可接受的条件

 namespace WorkService { public partial class WorkService : ServiceBase { protected const int sleepMinutes = 30; protected System.Timers.Timer _interval; protected bool _running = false; public WorkService() { InitializeComponent(); _interval = new System.Timers.Timer(); _interval.Elapsed += new ElapsedEventHandler(OnTimedEvent); _interval.Interval = sleepMinutes * 60 * 1000; _running = false; } protected override void OnStart(string[] args) { _running = true; _interval.Enabled = true; } protected override void OnStop() { _interval.Enabled = false; _running = false; } private static void OnTimedEvent(object source, ElapsedEventArgs e) { if(_running) ActualWorkDoneHere(); } } } 

这是一种方法。 将以下变量添加到您的类:

 private readonly object syncObject = new object(); private bool stopping; private bool stopped = true; 

然后在OnStart中,你做这样的事情(我有一个帮助方法,在这个例子中做一些记录,“Run”方法做实际工作):

  public override void OnStart() { while (stopping) { Thread.Sleep(MSECS_SLEEP_FOR_STOP); } lock (syncObject) { // make sure task isn't already started if (!stopped) { Helper.WriteToLog(logger, Level.INFO, string.Format("{0} {1}", TASK_NAME, "is already started.")); return; } stopped = false; } // start task in new thread Thread thread = new Thread(Run); thread.Start(); Helper.WriteToLog(logger, Level.INFO, string.Format("{0} {1}", TASK_NAME, "was started.")); } 

执行线程工作的“运行”方法看起来像这样(processInterval将在运行之间等待多长时间,您可以在构造函数中设置它或只是硬编码):

  private void Run() { try { while (!stopping) { // do work here // wait for process interval DateTime waitStart = DateTime.Now; while (((DateTime.Now - waitStart).TotalMilliseconds < processInterval) && !stopping) { // give processing time to other threads Thread.Sleep(MSECS_SLEEP_FOR_CHECK); } } lock (syncObject) { stopped = true; stopping = false; } Helper.WriteToLog(logger, Level.INFO, string.Format("{0} {1}", TASK_NAME, "was stopped.")); } catch (Exception e) { // log the exception, but ignore it (ie don't throw it) Helper.LogException(logger, MethodBase.GetCurrentMethod(), e); } } 

然后在OnStop中,你会这样做:

  public override void OnStop() { lock (syncObject) { if (stopping || stopped) { Helper.WriteToLog(logger, Level.INFO, string.Format("{0} {1}", TASK_NAME, "is already stopped.")); return; } stopping = true; } } 

您可以使用锁定对象来防止在您的工作实际发生时停止线程…

  private static readonly object _syncRoot = new object(); protected override void OnStop() { lock (_syncRoot) { _workerThread.Abort(); } } static void DoWork() { int sleepMinutes = 30; while (true) { lock (_syncRoot) { ActualWorkDoneHere(); } System.Threading.Thread.Sleep(new TimeSpan(0, sleepMinutes, 0)); } } 

你应该小心 – 如果你的ActualWorkDoneHere()函数花费的时间太长,windows会报告服务没有停止。

尝试使用autoreset标志来处理服务的停止。 在这种情况下,您不必执行线程中止。 已添加以下示例代码

 namespace WorkService { public partial class WorkService : ServiceBase { AutoResetEvent serviceStopEvent = new AutoResetEvent( false); public WorkService() { InitializeComponent(); } protected override void OnStart(string[] args) { Thread workerThread = new Thread(new ThreadStart(DoWork)); workerThread.Start(); } protected override void OnStop() { serviceStopEvent.Set(); } static void DoWork() { int sleepMinutes = 30; WaitHandle[ ] handles = new WaitHandle[ ] { serviceStopEvent }; while (WaitHandle.WaitAny( handles)) { ActualWorkDoneHere(); } } } } 

干杯,巴拉特。

我的服务侦听网络套接字,所以我所做的是创建一对连接的网络套接字并使用select系统调用来监听两者。 如果联接对报告准备好读取,我知道要关闭该服务。

这个技巧可用于触发任意数量的线程关闭,只要它们中没有一个实际从连接对中读取。

 while (true) { if (m_reset.WaitOne(1,false)) break; // DoSomething } 

请在内部尝试这个onStop()