我正在维护一些看起来像这样的代码。这是一项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));
}
}
}
}
答案 0 :(得分:22)
当我有这样的事情时,我通常使用ManualResetEvent
。这是在Stop()
电话中设置的。然后我等待超时:
for (;;)
{
if (_stop.WaitOne(timeout))
break;
DoSomething();
}
答案 1 :(得分:2)
自己实施是唯一安全的选择。即使你找到一种方法来确定一个线程是否正在休眠,如果你试图杀死它,你仍然会有竞争条件(因为它可能会在你检查之后和杀死之前开始处理)。
而不是Thread.Sleep你可以例如睡眠500毫秒并检查中止标志是否仍然是假,在30分钟之前再睡500毫秒等,然后完成工作等等(这将是一种实用的方法)。如果你想要更优雅的东西,你可以使用带有超时的ManualResetEvent来等待主线程发出中止它的时间。
答案 2 :(得分:2)
比赛: 最初的帖子在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();
}
}
}
答案 3 :(得分:1)
这是一种方法。将以下变量添加到您的类中:
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."));
}
执行线程工作的“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);
}
}
然后在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;
}
}
答案 4 :(得分:1)
您可以使用锁定对象来防止在您的工作实际发生时停止线程......
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会报告该服务没有停止。
答案 5 :(得分:0)
尝试使用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();
}
}
}
}
干杯, 巴拉斯。
答案 6 :(得分:0)
我的服务侦听网络套接字,所以我所做的是创建一对连接的网络套接字,并使用select系统调用来监听两者。如果联合对报告准备好阅读,我知道要关闭该服务。
这个技巧可用于触发任意数量的线程关闭,只要它们中没有一个实际读取连接对。
答案 7 :(得分:0)
while (true)
{
if (m_reset.WaitOne(1,false))
break;
// DoSomething
}
请在onStop()