我有一个Windows服务,它使用生产者/消费者队列模型,多个工作线程处理队列中的任务。这些任务可以运行很长时间,如果不是几小时,则按照很多分钟的顺序运行,不涉及循环。
我的问题是关于处理服务停止的最佳方法,以优雅地结束对这些工作线程的处理。我在另一个SO question中读到使用thread.Abort()是设计不良的标志,但似乎服务OnStop()方法在服务终止之前只有有限的时间完成。我可以在catchA中为ThreadAbortException做足够的清理(没有不一致状态的危险)所以在工作线程上调用thread.Abort()对我来说似乎没问题。是吗?有哪些替代方案?
答案 0 :(得分:6)
确实应该避免使用Abort
。最好给他们一些时间优雅地退出 - 然后也许在超时后,也许考虑中止他们 - 但最终,服务停止可以通过杀死进程来做到这一点。
我会尝试在队列中显示“刷新并退出”的信号 - 非常类似于Close
方法here,但在完成后会显示某种信号。
如果诉诸Abort
- 请考虑致命受伤的过程。尽快杀死它。
答案 1 :(得分:5)
为“shutdown”创建一个任务类型,并为每个工作线程将其注入生产者/消费者队列一次。
然后使用Thread.Join(超时)确保关闭已完成。
答案 2 :(得分:2)
使用.NET 4.0,您可以利用System.Threading.Tasks
命名空间来利用Task
对象。简而言之,您可以为任务分配CancellationToken
以更优雅地处理取消/中止,无论是长期还是短期。
有关MSDN的详细信息,请参阅here。
答案 3 :(得分:1)
实际修改的问题实际上与线程无关,而与如何停止长时间运行有关。我个人总是使用APM进行冗长的流和通信活动,如大文件传输。每个回调都在IO完成池线程上运行,并快速完成,处理适度的块并安排下一次传递。只需通过调用套接字对象上的Close()
即可取消挂起操作。这比DIY线程管理更便宜,更有效率。
正如已经提到的,Abort()
是不好的业力,应该避免。
以下材料是在将循环案例排除在问题之前编写的。
当长时间运行的进程循环时,它们应该 all 在其循环条件中包含一个退出标志,以便您可以发出信号退出。
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;
严格来说,你应该使用挥发物,但由于只有控制逻辑设定旗帜才无关紧要。从技术上讲,存在一种竞争条件,但这只意味着你可能会再次出现这种情况。
答案 4 :(得分:0)
使用ManualResetEvent检查事件是否已发出信号,请参阅Thread Synchronization (C# Programming Guide)上的示例
答案 5 :(得分: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;
}
}
答案 6 :(得分:0)
如果您的进程正在关闭,我个人看不到使用Abort()的问题。我会尝试找另一种方法,但最后,无论如何如果你将在主线程中进行清理无关紧要。
另一种选择是将工作线程标记为background个线程。这样,它们会在进程关闭时自动关闭。您可以在退出之前使用AppDomain.ProcessExit事件进行清理。