在 Windows 服务停止时优雅地关闭前台线程 [英] Close foreground thread gracefully on windows service stop
本文介绍了在 Windows 服务停止时优雅地关闭前台线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
在我的 Windows 服务中,我创建了一个父"前台线程,该线程又使用 ThreadPool(这意味着它们是后台)生成子"线程来执行任务.
In my windows service I create one "parent" foreground thread that in turn spawns "child" threads using ThreadPool (which means they are background) to execute tasks.
在 Windows 服务停止时优雅地关闭前台线程的最佳方法是什么?
What is the best way to close foreground thread gracefully on windows service stop?
这是我当前的实现(去除了特定于任务的逻辑):
Here is my current implementation (stripped out of task-specific logic):
public partial class TaskScheduler : ServiceBase
{
private static AutoResetEvent _finishedTaskAutoResetEvent = new AutoResetEvent(false);
//This flag is used to increase chances of the Spawning Thread to finish gracefully when service stops.
private bool StopRequested { get; set; }
private int _executingTasksCount;
private int ExecutingTasksCount { get { return _executingTasksCount; } }
private void IncCurrentTasksCount()
{
Interlocked.Increment(ref _executingTasksCount);
}
private void DecCurrentTasksCount()
{
Interlocked.Decrement(ref _executingTasksCount);
}
public TaskScheduler()
{
InitializeComponent();
Thread spawningThread = new Thread(DoSpawnTaskExecutionThreads);
spawningThread.Name = "Spawning Thread";
spawningThread.IsBackground = false;
spawningThread.Start();
}
protected override void OnStart(string[] args)
{
}
protected override void OnStop()
{
StopRequested = true;
}
private void DoSpawnTaskExecutionThreads()
{
//We check StopRequested to try and finish this thread gracefully when service stops.
while (!StopRequested)
{
while (!StopRequested && ExecutingTasksCount < MaxPooledTasks)
{
ThreadPool.QueueUserWorkItem(ExecuteTask, new Task());
IncCurrentTasksCount();
}
_finishedTaskAutoResetEvent.WaitOne();
}
//Either all task execution threads will finish or the process will be terminated forcibly.
while (ExecutingTasksCount > 0)
{
Thread.Sleep(200); //Check five times a second.
}
_eventLog.WriteEntry("The Spawning Thread finished along with task execution threads.");
}
private void ExecuteTask(object state)
{
try
{
Task task = (Task)state;
task.Execute();
}
catch
{
// Handle exception.
}
finally
{
DecCurrentTasksCount();
_finishedTaskAutoResetEvent.Set();
}
}
}
推荐答案
我发现代码有几个问题.
I see a couple of problems with the code.
StopRequested
的检查不是线程安全的.ExecutingTaskCount
的检查不是线程安全的.- 由于
_finishedTaskAutoResetEvent
是AutoResetEvent
信号可能会丢失,因为WaitHandle
不维护计数.也许这就是您想要的,但它可能会导致嵌套的while
循环出现一些奇怪的旋转.
- The check of
StopRequested
is not thread-safe. - The check of
ExecutingTaskCount
is not thread-safe. - Since
_finishedTaskAutoResetEvent
is anAutoResetEvent
signals can get lost because thatWaitHandle
does not maintain a count. Maybe that is what you want, but it could result in some strange spinning of the nestedwhile
loops.
这是我将如何重构您的代码.它使用 .NET 4.0 中可用的 CountdownEvent
类.
Here is how I would refactor your code. It uses the CountdownEvent
class which is available in .NET 4.0.
public class TaskScheduler : ServiceBase
{
private m_Stop as ManualResetEvent = new ManualResetEvent(false);
protected override void OnStart(string[] args)
{
var thread = new Thread(DoSpawnTaskExecutionThreads);
thread.Name = "Spawning Thread";
thread.IsBackground = false;
thread.Start();
}
protected override OnStop()
{
m_Stop.Set();
}
public DoSpawnTaskExecutionThreads()
{
// The semaphore will control how many concurrent tasks can run.
var pool = new Semaphore(MaxPooledThreads, MaxPooledThreads);
// The countdown event will be used to wait for any pending tasks.
// Initialize the count to 1 so that we treat this thread as if it
// were a work item. This is necessary to avoid a subtle race
// with a real work item that completes quickly.
var tasks = new CountdownEvent(1);
// This array will be used to control the spinning of the loop.
var all = new WaitHandle[] { pool, m_Stop };
while (WaitHandle.WaitAny(all) == 0)
{
// Indicate that there is another task.
tasks.AddCount();
// Queue the task.
Thread.QueueUserWorkItem(
(state) =>
{
try
{
var task = (Task)state;
task.Execute();
}
finally
{
pool.Release(); // Allow another task to be queued.
tasks.Signal(); // Indicate that this task is complete.
}
}, new Task());
}
// Indicate that the main thread is complete.
tasks.Signal();
// Wait for all pending tasks.
tasks.Wait();
}
}
这篇关于在 Windows 服务停止时优雅地关闭前台线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文