我实际上在 VS 2012/.NET 4.5 中开发了一个 Windows 服务。
该服务遵循以下代码片段的方案:
我担心的是,如果有人通过管理控制台停止服务,它可能只是在服务正在执行的过程中。
我已经阅读了一些关于通过请求停止停止 Windows 服务的文章,但我有点迷茫。有时会创建 WorkerThreads,有时会创建 ManualResetEvents,但到目前为止,我无法完全掌握 Windows 服务的最佳前进方向。
在停止 Windows 服务之前,我需要等到 onStop 方法中的处理正确完成。
那么最好的方法是什么,同时考虑下面的代码片段?
谢谢大家!
namespace ImportationCV
{
public partial class ImportationCV : ServiceBase
{
private System.Timers.Timer _oTimer;
public ImportationCV()
{
InitializeComponent();
if (!EventLog.SourceExists(DAL.Utilities.Constants.LOG_JOURNAL))
{
EventLog.CreateEventSource(DAL.Utilities.Constants.LOG_JOURNAL, DAL.Utilities.Constants.SOURCE_JOURNAL);
}
EventLog.Source = DAL.Utilities.Constants.SOURCE_JOURNAL;
EventLog.Log = DAL.Utilities.Constants.LOG_JOURNAL;
}
protected override void OnStart(string[] args)
{
int intDelai = Properties.Settings.Default.WatchDelay * 1000;
_oTimer = new System.Timers.Timer(intDelai);
_oTimer.Elapsed += new ElapsedEventHandler(this.Execute);
_oTimer.Start();
EventLog.WriteEntry(DAL.Utilities.Constants.LOG_JOURNAL, "Service " + DAL.Utilities.Constants.SERVICE_TITLE + " started at " + DateTime.Now.ToString("HH:mm:ss"), EventLogEntryType.Information);
}
protected override void OnStop()
{
if (_oTimer != null && _oTimer.Enabled)
{
_oTimer.Stop();
_oTimer.Dispose();
}
EventLog.WriteEntry(DAL.Utilities.Constants.LOG_JOURNAL, "Service " + DAL.Utilities.Constants.SERVICE_TITLE + " stopped at " + DateTime.Now.ToString("HH:mm:ss"), EventLogEntryType.Information);
}
private void Execute(object source, ElapsedEventArgs e)
{
_oTimer.Stop();
try
{
//Process
}
catch (Exception ex)
{
EventLog.WriteEntry(DAL.Utilities.Constants.LOG_JOURNAL, (ex.StackTrace + ("\r\n" + ex.Message)), EventLogEntryType.Error);
}
_oTimer.Start();
}
}
}
最佳答案
作为测试用例,我调用 System.Threading.Thread.Sleep(500000)
在 OnStop()
我的 Windows 服务的回调。我启动了服务,然后停止了它。我看到带有进度条的窗口,表明服务控制管理器 (SCM) 正在尝试停止服务。大约 2 分钟后,我收到了 SCM 的回复:
关闭此窗口后,我在 SCM 中的服务状态更改为正在停止,并且我注意到该服务继续在任务管理器中运行。 sleep 结束后(将近 6 分钟后),该过程停止。刷新 SCM 窗口显示服务不再运行。
我从这件事中拿走了几件事。一、OnStop()
应该真正尝试及时停止服务,作为与系统保持良好关系的一部分。二、看你如何OnStop()
方法是结构化的,您可以强制服务忽略抢占式停止请求,而不是在您这么说时停止。不建议这样做,但您似乎可以这样做。
至于您的具体情况,您必须了解的是System.Timers.Timer.Elapsed
事件 fires on a ThreadPool
thread .根据定义,这是一个 background thread ,这意味着它不会保持应用程序运行。当服务被告知关闭时,系统将停止所有后台线程,然后退出进程。因此,尽管 SCM 告诉您要关闭,但您仍担心将处理保持到完成为止 不能 发生的方式你目前的结构。为此,您需要创建一个正式的 System.Threading.Thread
对象,将其设置为前台线程,然后使用计时器触发该线程执行(而不是在 Elapsed
回调中完成)。
尽管如此,我仍然认为你会想要很好地使用系统,这意味着在请求时及时关闭服务。例如,如果您需要重新启动机器,会发生什么情况?我还没有测试过,但是如果你强制你的服务继续运行直到处理完成,系统可能确实会等到进程完成后再实际重新启动。这不是我想要的服务。
所以我会建议两件事之一。第一种选择是将处理分成可以单独完成的不同块。当每个块完成时,检查服务是否停止。如果是这样,请优雅地退出线程。如果这不能做到,那么我会介绍一些类似于交易的东西来处理。假设您需要与一堆数据库表进行交互,并且一旦开始就中断流程就会出现问题,因为数据库可能处于不良状态。如果数据库系统允许事务,这将变得相对容易。如果没有,则在内存中进行所有处理并在最后一秒提交更改。这样,您只会在提交更改时阻止关闭,而不是在整个持续时间内阻止。就其值(value)而言,我更喜欢使用 ManualResetEvent
用于将关闭命令传达给线程。
为了避免再啰嗦,我就在这里打断一下。哈。
编辑:
这是现成的,所以我不会验证它的准确性。我会解决您(或其他人)可能发现的任何问题。
定义两个 ManualResetEvent
对象,一个用于关闭通知,一个用于处理通知,以及 Thread
目的。更改 OnStart()
回调到这个:
using System.Threading;
using Timer = System.Timers.Timer; // both Threading and Timers have a timer class
ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
ManualResetEvent _processEvent = new ManualResetEvent(false);
Thread _thread;
Timer _oTimer;
protected override void OnStart(string[] args)
{
// Create the formal, foreground thread.
_thread = new Thread(Execute);
_thread.IsBackground = false; // set to foreground thread
_thread.Start();
// Start the timer. Notice the lambda expression for setting the
// process event when the timer elapses.
int intDelai = Properties.Settings.Default.WatchDelay * 1000;
_oTimer = new Timer(intDelai);
_oTimer.AutoReset = false;
_oTimer.Elapsed += (sender, e) => _processEvent.Set();
_oTimer.Start();
}
更改您的
Execute()
回调到这样的事情:private void Execute()
{
var handles = new WaitHandle[] { _shutdownEvent, _processEvent };
while (true)
{
switch (WaitHandle.WaitAny(handles))
{
case 0: // Shutdown Event
return; // end the thread
case 1: // Process Event
Process();
_processEvent.Reset(); // reset for next time
_oTimer.Start(); // trigger timer again
break;
}
}
}
创建
Process()
像这样的方法:private void Process()
{
try
{
// Do your processing here. If this takes a long time, you might
// want to periodically check the shutdown event to see if you need
// exit early.
}
catch (Exception ex)
{
// Do your logging here...
// You *could * also shutdown the thread here, but this will not
// stop the service.
_shutdownEvent.Set();
}
}
最后,在
OnStop()
回调,触发线程关闭:protected override void OnStop()
{
_oTimer.Stop(); // no harm in calling it
_oTimer.Dispose();
_shutdownEvent.Set(); // trigger the thread to stop
_thread.Join(); // wait for thread to stop
}
关于c# - Windows 服务 OnStop 等待完成处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22534330/