.NET4.8下用Topshelf转控制台为Windows服务,定时逻辑是否正确?
你的实现存在几个问题,修正后才能满足需求
核心问题:定时器间隔设置错误
System.Timers.Timer的构造参数单位是毫秒,你写的60只会让定时器每60毫秒触发一次,完全不符合每分钟执行的需求。需要改成60 * 1000(即60000毫秒)。
构造函数内的同步调用风险
你在APIService构造函数里直接调用SAPAPI.PostRequest(uri)和SLMAPI.PostRequest(uri),这会导致服务启动时同步执行这些操作:
- 如果这些请求耗时较长,可能触发Windows服务的启动超时(默认服务启动超时是30秒),导致服务启动失败
- 如果请求失败,可能直接导致服务初始化异常
建议把这部分逻辑移到Start方法中,或者放到定时器的执行逻辑里(比如每次定时器触发时执行),避免阻塞服务启动流程。
文件写入的权限与目录检查
C:\ProgramData\temp\目录如果不存在,File.AppendAllLines会抛出异常;另外LocalSystem账号虽然权限较高,但也要确保该目录的读写权限正常。建议在写入前先检查并创建目录:
var logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "temp"); if (!Directory.Exists(logDir)) { Directory.CreateDirectory(logDir); } var logPath = Path.Combine(logDir, "Timer.txt"); File.AppendAllLines(logPath, new[] { DateTime.Now.ToString() });
修正后的APIService代码示例
public class APIService { private System.Timers.Timer _timer; private readonly string _uri; // 建议通过构造注入或配置获取uri,避免硬编码 public APIService(string uri) { _uri = uri; // 初始化定时器,设置为每分钟触发一次 _timer = new System.Timers.Timer(60 * 1000) { AutoReset = true, Enabled = false // 先不启用,在Start方法中启动 }; _timer.Elapsed += TimerElapsed; } private void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e) { try { // 执行定时任务 SAPAPI.PostRequest(_uri); SLMAPI.PostRequest(_uri); // 记录执行日志 var logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "temp"); if (!Directory.Exists(logDir)) { Directory.CreateDirectory(logDir); } var logPath = Path.Combine(logDir, "Timer.txt"); File.AppendAllLines(logPath, new[] { DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") }); } catch (Exception ex) { // 添加异常日志,避免任务失败导致服务崩溃 using (var eventLog = new EventLog("Application")) { eventLog.Source = "ClientWindowService"; eventLog.WriteEntry($"任务执行失败: {ex.Message}", EventLogEntryType.Error); } } } public void Start() { // 如果需要服务启动时立即执行一次任务,取消下面注释 // TimerElapsed(null, null); _timer.Start(); } public void Stop() { _timer.Stop(); _timer.Dispose(); // 释放定时器资源 } }
Program.cs的小调整
如果构造函数需要传入uri,调整ConstructUsing部分:
a.ConstructUsing(apiservice => new APIService("你的目标uri地址"));
总结
修正定时器间隔后,代码才能实现每分钟触发一次任务;同时调整构造函数内的同步调用、添加异常处理和目录检查,可以让服务运行更稳定。
内容的提问来源于stack exchange,提问作者Flickery
相关产品推荐
相关产品推荐

