如何安全地停止在 Windows 服务中运行的 C# .NET 线程?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1764898/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I safely stop a C# .NET thread running in a Windows service?
提问by Stimy
I am maintaining some code which looks something like this. It's a Windows service which does some work every 30 minutes. The ActualWorkDoneHere method takes about 30 seconds to run, but if it is stopped while running it can leave things in a bad state. What is the best way to prevent that from happening? Should I replace the While(true) with a boolean which is set to false in the onstop method (removing the thread Abort call)? Is there some way to tell if a thread is sleeping?
我正在维护一些看起来像这样的代码。它是一个 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));
}
}
}
}
采纳答案by Roger Lipscombe
When I have something like this, I usually use a ManualResetEvent
. This is set in the Stop()
call. Then I wait with a timeout:
当我有这样的事情时,我通常使用ManualResetEvent
. 这是在Stop()
调用中设置的。然后我等待超时:
for (;;)
{
if (_stop.WaitOne(timeout))
break;
DoSomething();
}
回答by Grzenio
Implementing it yourself is the only safe option. Even if you find a way to find out if a thread is sleeping, you will still have a race condition if you try to kill it (because it potentially starts processing after you check and before you kill it).
自己实现它是唯一安全的选择。即使您找到了一种方法来确定线程是否正在休眠,如果您尝试杀死它,您仍然会遇到竞争条件(因为它可能会在您检查之后和杀死它之前开始处理)。
Instead of Thread.Sleep you could e.g. sleep 500ms and check if the abort flag is still false, sleep another 500ms etc. before 30mins passes, then do the job, etc. (this would be a pragmatic approach). If you want something more elegant, you could use a ManualResetEvent with a timeout to wait for the main thread signalling that its time to abort.
代替 Thread.Sleep 您可以例如休眠 500 毫秒并检查中止标志是否仍然为假,在 30 分钟过去之前再休眠 500 毫秒等,然后完成工作等(这将是一种务实的方法)。如果你想要更优雅的东西,你可以使用一个带有超时的 ManualResetEvent 来等待主线程发出它的时间中止的信号。
回答by Bharath K
Try using an autoreset flag to handle stopping of the service. In that case you would not have to perform thread abort. Have added the sample code below
尝试使用自动重置标志来处理服务的停止。在这种情况下,您不必执行线程中止。已经添加了下面的示例代码
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();
}
}
}
}
Cheers, Bharath.
干杯,巴拉特。
回答by dcp
Here's one way to do it. Add the following variables to your class:
这是一种方法。将以下变量添加到您的类中:
private readonly object syncObject = new object();
private bool stopping;
private bool stopped = true;
Then in OnStart, you do something like this (I have a helper method which does some logging in this example, and the "Run" method does the actual work).:
然后在 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."));
}
Your "Run" method, which does the work of the thread, would look like this (processInterval would be how long you want to wait between runs, you could set it in the constructor or just hardcode it):
您执行线程工作的“Run”方法如下所示(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 (i.e. don't throw it)
Helper.LogException(logger, MethodBase.GetCurrentMethod(), e);
}
}
Then in OnStop, you would do this:
然后在 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;
}
}
回答by Richard
You could use a lock object to prevent the thread being stopped while your work is actually happening...
您可以使用锁定对象来防止线程在您的工作实际发生时停止......
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));
}
}
You should be careful though - if your ActualWorkDoneHere()
function takes too long, windows will report the service as failing to stop.
不过你应该小心——如果你的ActualWorkDoneHere()
函数花费的时间太长,windows 会报告该服务无法停止。
回答by JeffreyABecker
Wow everyone makes this so complicated. Use a Timer:
哇,每个人都让这变得如此复杂。使用定时器:
On races: The original post had a race in OnStop which has been fixed. As far as I know putting the service into a stopped state will not abort threadpool threads which are used to service the timer. The condition of the timer firing and the service being stopped at the same time is irrelevant. ActualWorkDoneHere() will either run, or not run. Both are acceptable conditions.
关于比赛:原帖在 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();
}
}
}
回答by Joshua
My service listens on a network socket so what I did is create a joined pair of network sockets and used the select system call to listen on both. If the joined pair reported ready to read I knew to shutdown the service.
我的服务侦听网络套接字,所以我所做的是创建一对连接的网络套接字并使用 select 系统调用来侦听两者。如果加入的对报告已准备好阅读,我知道要关闭该服务。
This trick can be used to trigger an arbitrary number of threads to shut down so long as none of them actually read from the connected pair.
这个技巧可以用来触发任意数量的线程关闭,只要它们实际上都没有从连接对中读取。
回答by Robin Gupta
while (true)
{
if (m_reset.WaitOne(1,false))
break;
// DoSomething
}
Please Try this inside onStop()
请在里面试试这个 onStop()