【问题标题】:windows service doesn't run at regular intervalsWindows 服务不定期运行
【发布时间】:2016-08-18 09:35:59
【问题描述】:

我已经开发了 windows 服务,用于每两分钟检查一些服务是否运行。如果服务没有运行,则自动启动它们。

这是我的代码

public partial class Service1 : ServiceBase
    {
        public Service1()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {

            this.CheckServices();

            this.ScheduleService();


        }

        protected override void OnStop()
        {
            this.Schedular.Dispose();
        }




        public void CheckServices()
        {

            log4net.Config.BasicConfigurator.Configure();
            log4net.ILog log = log4net.LogManager.GetLogger(typeof(Service1));
            try
            {

                string[] arr1 = new string[] { "CaseWorksCachingService", "Te.Service" };
                for (int i = 0; i < arr1.Length; i++)
                {
                    ServiceController service = new ServiceController(arr1[i]);

                    service.Refresh();

                    if (service.Status == ServiceControllerStatus.Stopped)
                    {
                        service.Start();

                    }
                }


            }
            catch (Exception ex)
            {
                log.Error("Error Message: " + ex.Message.ToString(), ex);
            }


        }

        //ScheduleService Method

        private Timer Schedular;


        public void ScheduleService()
        {
            try
            {
                Schedular = new Timer(new TimerCallback(SchedularCallback));
                string mode = ConfigurationManager.AppSettings["Mode"].ToUpper();


                //Set the Default Time.
                DateTime scheduledTime = DateTime.MinValue;



                if (mode.ToUpper() == "INTERVAL")
                {
                    //Get the Interval in Minutes from AppSettings.
                    int intervalMinutes = Convert.ToInt32(ConfigurationManager.AppSettings["IntervalMinutes"]);

                    //Set the Scheduled Time by adding the Interval to Current Time.
                    scheduledTime = DateTime.Now.AddMinutes(intervalMinutes);
                    if (DateTime.Now > scheduledTime)
                    {
                        //If Scheduled Time is passed set Schedule for the next Interval.
                        scheduledTime = scheduledTime.AddMinutes(intervalMinutes);
                    }
                }

                TimeSpan timeSpan = scheduledTime.Subtract(DateTime.Now);


                //Get the difference in Minutes between the Scheduled and Current Time.
                int dueTime = Convert.ToInt32(timeSpan.TotalMilliseconds);

                //Change the Timer's Due Time.
                Schedular.Change(dueTime, Timeout.Infinite);
            }
            catch (Exception ex)
            {


                //Stop the Windows Service.
                using (System.ServiceProcess.ServiceController serviceController = new System.ServiceProcess.ServiceController("MyFirstService"))
                {
                    serviceController.Stop();
                }
            }
        }

        private void SchedularCallback(object e)
        {
            //this.WriteToFile("Simple Service Log: {0}");
            this.CheckServices();
            this.ScheduleService();
        }
    }

这是我的 app.config 文件

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key ="Mode" value ="Interval"/>
    <!-- <add key ="Mode" value ="Interval"/>-->
    <add key ="IntervalMinutes" value ="2"/>

  </appSettings>

  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net, Version=2.0.5, Culture=neutral, PublicKeyToken=1b44e1d426115821" />
  </configSections>
  <!-- Log4net Logging Setup -->
  <log4net>
    <appender name="FileAppender" type="log4net.Appender.FileAppender,log4net">
      <file value="C:\\mylogfile1.txt" />
      <!-- the location where the log file would be created -->
      <appendToFile value="true" />
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %level %logger - %message%newline" />
      </layout>
      <filter type="log4net.Filter.LevelRangeFilter">
        <levelMin value="INFO" />
        <levelMax value="FATAL" />
      </filter>
    </appender>
    <root>
      <level value="DEBUG" />
      <appender-ref ref="FileAppender" />
    </root>
  </log4net>
</configuration>

错误:“本地计算机上的 Windows Search 服务启动然后停止。如果某些服务没有被其他服务或程序使用,它们会自动停止”

【问题讨论】:

  • 你在ScheduleService的catch块中停止了服务——好吧,一个服务——没有记录原因。
  • 好吧,如果 ScheduleService 内部出现问题,您将永远不会发现它,因为您吞下了异常(没有真正的原因,因为未处理的异常无论如何都会导致服务中断,无需您编写任何代码)
  • 嗨@stuartd,如果调度中出现任何异常,我将停止服务。是不是错了,我不知道是错误。你能建议我在我的代码中哪里做错了吗?
  • 嗨@Damien_The_Unbeliever,建议我在我的代码中哪里做错了。
  • 我们不知道。你不知道。没人知道。作为第一步,要么在ScheduleService 内的catch 内添加一行日志记录,要么(更好)完全删除try/catch 块。

标签: c# .net windows-services


【解决方案1】:

很可能会发生导致您的服务过早退出的异常。这是值得研究的。但是,我怀疑您的问题的实际原因是您的服务在执行 OnStart() 方法后立即退出。 Windows 服务不会自动保持运行。如果OnStart() 方法没有启动保持服务运行的前台线程,您将看到您收到的确切错误。我尝试在本地控制台应用程序中执行您的代码,一旦OnStart() 方法完成,控制台应用程序就会退出。

要在服务关闭之前保持服务运行,请在 OnStart() 方法中启动前台线程。以下是一个工作示例。

using System.Threading;
private ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
private Thread _thread;

protected override void OnStart(string[] args)
{
    _thread = new Thread(WorkerThreadFunc);
    _thread.Name = "My Worker Thread";
    _thread.IsBackground = false;
    _thread.Start();
}

protected override void OnStop()
{
    this.Schedular.Dispose();
    this._shutdownEvent.Set();
    this._thread.Join(3000);
}

private void WorkerThreadFunc()
{
    this.CheckServices();
    this.ScheduleService();

    // This while loop will continue to run, keeping the thread alive,
    // until the OnStop() method is called.
    while (!_shutdownEvent.WaitOne(0))
    {
        Thread.Sleep(1000);
    }
}

没有其他需要改变的。

如果要调试服务,请在 OnStart() 方法的开头调用 System.Diagnostics.Debugger.Launch() 并在调试中编译。启动服务时,系统会提示您启动调试会话。假设您在机器上有足够的权限,您应该能够在该点设置断点并正常调试。

HTH

【讨论】:

  • 计时器实际上应该保持服务运行,因为它已经产生了一个新线程?
  • 这是一个后台线程,不会让进程保持运行。
  • 那么他就可以使用System.Timers.Timer了。
  • 不,System.Threading.Timer 和 System.Timers.Timer 都在线程池上执行回调,根据定义,线程池是后台线程。要看到这一点,请创建一个简单的控制台应用程序,在 Main() 中创建任一计时器,然后启动它。当您从 Main() 返回时,应用程序退出,因为不再有前台保持进程处于活动状态。相信我。 Windows 服务需要一个前台线程来保持运行。讨论的两个计时器是不够的。
  • 嗨@MattDavis,我是否需要SchedularCallback 方法。
【解决方案2】:

您的服务的问题正是马特戴维斯在他的回答中所说的:只有在OnStart 中产生了前台线程时,服务才会保持运行。这就是他的代码正在做的事情。它只是创建一个基本上除了等待什么都不做的线程,但这足以让服务保持运行。

除了一件事之外,您的其余代码可以保持不变。更改您的ScheduleService 方法如下:

   public void ScheduleService()
    {
        try
        {
            // Only create timer once
            if (Schedular != null)
                Schedular = new Timer(new TimerCallback(SchedularCallback));

            // Use proper way to get setting
            string mode = Properties.Settings.Default.Mode.ToUpper();

            ...

            if (mode == "INTERVAL")
            {
                // Use proper way to get settings
                int intervalMinutes = Properties.Settings.Default.IntervalMinutes;

                ...

我知道有些人可能不认为这是一个答案,但调试似乎是这里的关键,因此我将告诉你我找到了一种调试服务的简单方法。

第 1 步
使用两种方法扩展您的服务:一种调用OnStart,另一种调用OnStop,因为默认方法受到保护。

public void DoStart(string[] args)
{
    OnStart(xyz);
}

public void DoStop()
{
    OnStop();
}

第 2 步
添加一个简单的 GUI。没有任何控件的 WinForms 表单就足够了。扩展构造函数以获取服务类的实例。当表单加载“启动您的服务”时,当它关闭时,“停止您的服务”。根据您创建表单的方式,您可能需要在项目中手动引用System.Windows.FormsSystem.Drawing

例子:

public class ServiceGUI : System.Windows.Forms.Form
{
    private MyService _myService;

    public ServiceGUI(MyService service)
    {
        _myService = service;
    }

    protected override void OnShown(EventArgs e)
    {
        base.OnShown(e);
        _myService.DoStart(null);
    }

    protected override void OnClosing(CancelEventArgs e)
    {
        base.OnClosing(e);
        _myService.DoStop();
    }        
}

第 3 步
让您的 Main 方法决定是作为服务运行还是作为 GUI 应用程序运行。方便的Environment.UserInteractive 属性允许您确定exe 是否由某个GUI 用户运行。

if (Environment.UserInteractive)
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new ServiceGUI(new MyService()));
}
else
{
    ServiceBase.Run(new ServiceBase[] { new MyService() });
}

你现在能做什么?
您可以像普通应用程序一样启动您的应用程序,并实际正确调试、设置断点、检查变量等。当然我知道您也可以将调试器附加到正在运行的进程,但在您的情况下,您没有运行过程,所以这是一个很好的解决方案,看看问题是什么。

您的服务启动代码之类的东西可能无法正常工作(除非您以管理员身份运行 Visual Studio),但这也可以很好地测试在您的服务启动时遇到错误时是否工作正常。

【讨论】:

  • 嗨@Thorsten Dittmar,你正在提供调试我的服务的代码。
  • 我知道。我目前正在添加我的答案。
  • 嗨,我掩饰一下,你提供给我的代码是用于调试或实现我的任务。
  • 我在第一个答案中提供的代码是为了帮助您调试服务以查找错误。我在顶部的改进答案试图解释我在您的代码中看到的问题,并提供了改进的版本。
  • 您好,我将实施服务并尽快回复您,如果我有任何疑问,感谢您提供有价值的信息。
猜你喜欢
  • 1970-01-01
  • 2011-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
相关资源
最近更新 更多