我实际上是在VS 2012 / .NET 4.5中开发Windows服务。
该服务遵循以下代码段的方案:
我担心的是,如果有人通过管理控制台停止服务,可能就是在服务正在进行的过程中。
我已经做了一些关于停止请求停止的Windows服务的阅读,但有点迷失。有时创建WorkerThreads,有时会创建ManualResetEvents,但到目前为止,我无法完全掌握Windows服务的最佳前进方法。
我需要等到处理在onStop方法中正确完成后再停止Windows服务。
那么最好的方法是什么,还要考虑下面的代码片段?
全部谢谢!
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();
}
}
}
答案 0 :(得分:19)
作为一个测试案例,我在我的Windows服务的System.Threading.Thread.Sleep(500000)
回调中调用OnStop()
。我启动了服务然后停止了它。我得到了带有进度条的窗口,指示服务控制管理器(SCM)正在尝试停止服务。大约2分钟后,我收到了SCM的回复:
在我关闭此窗口后,我在SCM中的服务状态变为停止,我注意到该服务继续在任务管理器中运行。睡眠过后(将近6分钟后),过程停止。刷新SCM窗口显示服务已不再运行。
我从这里拿走了一些东西。首先,OnStop()
应该真正尝试及时停止服务,就像玩好系统一样。其次,根据OnStop()
方法的结构,可以强制服务忽略抢先请求停止,而是在您这样做时停止。建议您不要这样做,但可以执行此操作。
至于您的具体情况,您必须了解的是System.Timers.Timer.Elapsed
事件fires on a ThreadPool
thread。根据定义,这是background thread,这意味着它不会使应用程序保持运行。当告知服务关闭时,系统将停止所有后台线程,然后退出该进程。因此,尽管被SCM告知关闭不能,但是你仍然担心将处理工作一直持续到现在已经结构化的方式。为此,您需要创建一个正式的System.Threading.Thread
对象,将其设置为前台线程,然后使用计时器触发此线程执行(而不是在{{1回调)。
所有这些都说,我仍然认为你想要与系统很好地配合,这意味着在要求时及时关闭服务。例如,如果您需要重启机器会发生什么?我还没有对它进行测试,但是如果你强制你的服务继续运行直到处理完成,那么系统可能会在实际重新启动之前等到进程完成。这不是我想要的服务。
所以我会建议两件事之一。第一种选择是将处理分解为可以单独完成的不同块。每个块完成后,检查服务是否正在停止。如果是这样,请正常退出线程。如果无法做到这一点,那么我会向您的处理介绍类似于交易的内容。让我们说你需要与一堆数据库表进行交互,并且一旦启动就中断流程就会出现问题,因为数据库可能处于不良状态。如果数据库系统允许事务处理,则相对容易。如果没有,那么在内存中执行所有处理并在最后一秒提交更改。这样,您只会在提交更改时阻止关闭,而不是在整个持续时间内阻塞。值得一提的是,我更喜欢使用Elapsed
将断点命令传递给线程。
为了避免进一步散步,我会在这里切断它。 HTH。
这不在袖口,所以我不会验证它的准确性。我会解决你(或其他人)可能遇到的任何问题。
定义两个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()
答案 1 :(得分:0)
@Matt - 感谢伟大的代码,非常有帮助。 如果我在_shutdownEvent上添加了另一个测试,我发现它的效果更好:
case 1: // Process Event
Process();
if(_shutdownEvent.WaitOne(0)) break; // don't loop again if a shutdown is needed
...