从某种意义上说,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 中,尝试net stop
在 10 秒前停止服务(使用 )。你应该看到:
服务正在启动或停止。请稍后再试。
所以启动服务显然是一个阻塞操作,必须在服务停止之前完成。
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 可以更轻松地快速启动和停止服务;但是该示例也与一个命令外壳类似地工作。
最后,您可能会发现 Mitchell Taylor (CoolDadTx)在 MSDN 论坛中对类似问题的回答和我一样有趣:
SCM 使用的线程模型没有正式记录 AFAIK。众所周知,每个服务都在自己的线程上被调用。但是,SCM 可能会也可能不会使用线程池来跨服务重用线程。当服务被调用(启动、停止、自定义命令等)时,它应该执行其任务并快速返回。可以花费多长时间有很大的限制。除了快速返回之外,您还需要将请求推送到辅助线程进行处理。SCM 本身在单独的线程上运行,因此如果服务响应时间过长,则 SCM 将其视为挂起。这在这里讨论:http: //msdn.microsoft.com/en-us/library/ms684264 (VS.85).aspx
更新:
特别注意Mitchell Taylor 引用的文章链接到的Service State Transitions MSDN 文章。它包含一个状态图,该状态图非常清晰且权威地记录了定义的服务状态转换,并与我上面概述的内容保持一致。它还与状态图相关地解释了 SCM 如何有时不传输服务控制请求以确保仅定义状态转换。