从某种意义上说,SCM 不能保证您概述的异常不会被抛出。它当然不控制服务对私人成员的操作 - 例如。如果其他服务代码影响_foo。
话虽如此,请考虑以下场景,看看为什么您的具体问题的答案显然是否定的:
1) 使用以下更改来构建您的服务以进行演示:
public partial class MyService : ServiceBase
{
private Object _foo;
private const string _logName = "MyService Log.txt"; // to support added logging
public MyService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
// demonstrative logging
var threadId = Thread.CurrentThread.ManagedThreadId;
using (var log = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + _logName, true))
{
log.WriteLine("{0}: In OnStart(string[]) on thread ID {1}. Sleeping for 10 seconds...", DateTime.Now, threadId);
}
// Sleep before initializing _foo to allow calling OnStop before OnStart completes unless the SCM synchronizes calls to the methods.
Thread.Sleep(10000);
_foo = new Object();
}
protected override void OnStop()
{
// demonstrative logging added
var threadId = Thread.CurrentThread.ManagedThreadId;
using (var log = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + _logName, true))
{
log.WriteLine("{0}: In OnStop() on thread ID {1}.", DateTime.Now, threadId);
}
if (_foo == null)
{
// demonstrative logging added
using (var log = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + _logName, true))
{
log.WriteLine("{0}: _foo == null", DateTime.Now);
}
throw new Exception("Assignment not visible"); // Can this happen?
}
_foo = null;
}
}
2) 打开命令外壳。
3) 打开另一个命令外壳。
4) 在第一个命令 shell 中,如果您还没有安装服务(使用 sc create),然后启动它(使用 net start)。你应该看到:
MyService 服务正在启动.....
当 SCM 等待 10 秒的睡眠时间以启动服务时,应一个接一个地添加尾随点。
5) 在第二个命令 shell 中,尝试在 10 秒前停止服务(使用net stop)。你应该看到:
服务正在启动或停止。请稍后再试。
所以启动服务显然是一个阻塞操作,必须在服务停止之前完成。
6) 10 秒后检查第一个命令 shell。你应该看到:
MyService 服务启动成功。
7) 返回第二个命令 shell 并再次尝试停止服务。你应该看到:
MyService 服务正在停止。
MyService 服务已成功停止。
8) 查看生成的日志 - 例如:
2013 年 10 月 22 日上午 7:28:55:在线程 ID 4 上的 OnStart(string[]) 中。休眠 10 秒...
2013 年 10 月 22 日上午 7:29:17:在线程 ID 5 上的 OnStop() 中。
我认为使用两个命令 shell 可以更轻松地快速启动和停止服务;但是这个例子也同样适用于一个命令 shell。
最后,您可能会发现 Mitchell Taylor (CoolDadTx) 对a similar question in MSDN forums 的回答和我一样有趣:
SCM 使用的线程模型没有正式记录在 AFAIK。众所周知,每个服务都在自己的线程上被调用。但是,SCM 可能会也可能不会使用线程池来跨服务重用线程。当服务被调用(启动、停止、自定义命令等)时,它应该执行其任务并快速返回。可以花费多长时间有很大的限制。除了快速返回之外,您还需要将请求推送到辅助线程进行处理。 SCM 本身在单独的线程上运行,因此如果服务响应时间过长,则 SCM 将其视为挂起。这在这里讨论:http://msdn.microsoft.com/en-us/library/ms684264(VS.85).aspx
更新:
特别注意Service State Transitions MSDN 文章,Mitchell Taylor 引用的文章链接到该文章。它包含一个状态图,该图非常清晰且权威地记录了定义的服务状态转换,并与我上面概述的内容保持一致。它还与状态图相关地解释了 SCM 如何不时传输服务控制请求以确保仅进行定义的状态转换。