我在.NET中开发了一个相当复杂的Windows服务(在2.0 CLR上运行),我注意到它消耗了大量的CPU周期,有效地最大化了一台机器的核心。
我附加了一个分析器(SlimTune),它报告了超过90%的CPU周期花费在ServiceBase.Run
。我的服务似乎运行正常,它创建了大约9-15个其他线程,这些线程都在EventWaitHandle.WaitOne
或Stread.Read
调用中被阻止(按设计)并且运行正常,我在程序中没有循环导致CPU烧毁的代码,因此ServiceBase.Run
会出现问题。
我看了一下反射器,我在Run
方法调用树中看不到任何循环,我的服务的Start
方法完成并将控制返回给它的调用者。
我的代码很简单,让它成为一个神秘的原因:
public static class Program {
public const String ServiceName = "FooService";
public static void Main(String[] args) {
using(FooService service = new FooService()) {
ServiceBase.Run( service );
}
}
}
public partial class FooService : ServiceBase {
private FooServiceImplementation _fooService;
public FooService() {
this.ServiceName = "FooService";
}
protected override void Dispose(Boolean disposing) {
base.Dispose( disposing );
if( disposing ) {
if( _fooService != null ) {
_fooService.Dispose();
_fooService = null;
}
}
}
protected override void OnStart(String[] args) {
try {
// Load configuration.
LoadConfiguration();
// DO NOT CALL Config.Trace before Config has loaded, because the ExceptionsPath isn't yet set.
// TODO: Use the temp directory if ExceptionsPath is not set.
if( Config.DebuggerBreakOnStart ) {
Debugger.Break();
}
Config.Trace("[OnStart] LoadConfiguration() complete.");
_fooService = new FooServiceLoop();
Config.Trace("[OnStart] _fooService instantiated.");
_fooService.Start();
Config.Trace("[OnStart] _fooService.Start() complete.");
} catch(Exception ex) {
Config.LogException( ex );
throw;
}
}
// Other methods omitted for brevity
我的日志显示正在调用Config.Trace("[OnStart] _fooService.Start() complete.");
并且正在返回OnStart
方法。
答案 0 :(得分:1)
ServiceBase.Run
方法告诉系统启动所有ServiceBase
派生的实例(或FooService
,在您的情况下)。它通过设置一些结构然后调用本机StartServiceCtrlDispatcher
函数来完成此操作。此函数进入处理服务控制消息的等待状态,并将其传递给ServiceBase
派生的实例(如果适用)。这意味着因为ServiceBase.Run是第一个执行的方法,并且在完成和处理所有其他代码之前不会退出,它似乎是整个应用程序中运行时间最长的方法。这是用词不当,因为它大部分时间都处于等待状态而不使用CPU,等待服务控制请求。 SlimTune告诉你在方法中花费的时间(作为在线程中花费的时间的百分比,参见CHM文件中的QuickStart部分),这只是减去方法从它开始时退出的时间。这与使用的CPU周期不同。 90%这意味着您服务中的代码实际上只占总运行时间的10%。
除非您不立即从OnStart
返回,否则在OnStop
中花费大量时间,或花费大量时间处理其他服务控制请求ServiceBase.Run
,在CPU周期方面没有太多使用。
注意:系统在ServiceBase
退出ServiceBase.Run
之前处理它们。因此,您不需要将ServiceBase
派生的实例放在using
语句中。我不认为这会给你带来任何问题;但这可能会导致ObjectDisposedException
。