定期运行的Windows服务

时间:2009-09-30 14:32:08

标签: c# windows-services multithreading backgroundworker

我正在编写一个Windows服务,一旦启动就会每X小时运行一次。它完成的过程相当密集,所以我想使用后台工作程序。我正在使用设置文件来存储运行和上次运行服务之间的小时数。

我不确定这样做的最佳方式 - 也就是说,我希望服务尽可能少地使用资源,当它运行时,它需要在后台工作程序中运行,报告它做了什么,然后回到空闲模式。

我考虑过使用2名背景工作者。第一个worker将是服务的私有局部变量,运行如下:

while (true)
{
      //create new background worker and run

      Thread.Sleep(Settings.Default.SleepTimeHours * 3600000);
}

使用在循环的每次迭代中创建的子工作程序,并在完成时销毁。为了支持取消,我想我必须在服务中有第二个工作者的本地实例,但如果当前进程没有运行,它将为null。当辅助工作程序完成时,它将发送我的报告,在设置文件中设置上次运行时间,然后处理该工作程序并将引用设置为null。

我想知道是否有更好的方法来做这个或最佳实践。

由于

13 个答案:

答案 0 :(得分:33)

我通常使用Timer,然后在进程开始运行时停止它。

这是一篇解释how to do it的文章。

答案 1 :(得分:15)

这不是一个好主意,因为你将线程锁定在整个“SleepTimeHours”期​​间,你甚至无法在此期间停止服务。

你可以提供这项服务,以便它可以睡觉,例如5秒,然后检查是否是时候恢复工作,如果没有,再睡5秒钟(如果你需要停止服务,这会给你必要的响应)。

或者:您可能最好只编写一个可以使用Windows“计划任务”功能进行安排的控制台应用程序,以便每x小时运行一次。这样,如果您的应用没有做任何事情,您将不会阻止或使用任何系统资源......

马克

答案 2 :(得分:10)

考虑像Quartz.Net这样的作业调度程序。

http://quartznet.sourceforge.net/

答案 3 :(得分:7)

更像这样的事情:

    public class LongRunningService : ServiceBase
{
    System.Threading.Thread processThread;
    System.Timers.Timer timer;
    private Boolean Cancel;

    protected override void OnStart(string[] args)
    {
        timer = new Timer(Settings.Default.SleepTimeHours * 3600000);
        timer.Elapsed += new ElapsedEventHandler(timer_Tick);
        timer.Start();

        Cancel = false;
    }

    protected override void OnContinue()
    {
        timer.Start();
    }

    protected override void OnPause()
    {
        timer.Stop();
    }

    protected override void OnStop()
    {
        if (processThread.ThreadState == System.Threading.ThreadState.Running)
        {
            Cancel = true;
            // Give thread a chance to stop
            processThread.Join(500);
            processThread.Abort();
        }
    }

    void timer_Tick(object sender, EventArgs e)
    {
        processThread = new System.Threading.Thread(new ThreadStart(DoWork));
        processThread.Start();
    }

    private void DoWork()
    {
        try
        {
            while (!Cancel)
            {

            if (Cancel) { return; }
            // Do General Work

            System.Threading.Thread.BeginCriticalRegion();
            {
                // Do work that should not be aborted in here.
            }
            System.Threading.Thread.EndCriticalRegion();
            }

        }
        catch (System.Threading.ThreadAbortException tae)
        {
            // Clean up correctly to leave program in stable state.
        }
    }
}

答案 4 :(得分:5)

使用基于System.Threading.WaitHandle方法的方法。

using System.Threading;
private Thread _thread;
private ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
private ManualResetEvent _scheduleEvent = new ManualResetEvent(false);
private System.Timers.Timer _scheduleTimer = new System.Timers.Timer();
protected override void OnStart(string[] args)
{
    // Configure the timer.
    _scheduleTimer.AutoReset = false;
    _scheduleTimer.Interval = 120000; // 2 minutes in milliseconds
    _scheduleTimer.Elapsed += delegate { _scheduleEvent.Set(); }

    // Create the thread using anonymous method.
    _thread = new Thread( delegate() {
        // Create the WaitHandle array.
        WaitHandler[] handles = new WaitHandle[] {
            _shutdownEvent,
            _scheduleEvent
        };
        // Start the timer.
        _scheduleTimer.Start();
        // Wait for one of the events to occur.
        while (!_shutdownEvent.WaitOne(0)) {
            switch (WaitHandle.WaitAny(handles)) { 
               case 0:  // Shutdown Event
                   break;
               case 1:  // Schedule Event
                   _scheduleTimer.Stop();
                   _scheduleEvent.Reset();
                   ThreadPool.QueueUserWorkItem(PerformScheduledWork, null);
                   break;
               default:
                   _shutdownEvent.Set(); // should never occur
            }
        }
    } );
    _thread.IsBackground = true;
    _thread.Start();
}
protected override void OnStop()
{
    // Signal the thread to shutdown.
    _shutdownEvent.Set();
    // Give the thread 3 seconds to terminate.
    if (!_thread.Join(3000)) {
        _thread.Abort(); // not perferred, but the service is closing anyway
    }
}
private void PerformScheduledWork(object state)
{
    // Perform your work here, but be mindful of the _shutdownEvent in case
    // the service is shutting down.
    //
    // Reschedule the work to be performed.
     _scheduleTimer.Start();
}

答案 5 :(得分:2)

你真的不想使用后台工作者。当你在前台有一些东西(比如UI)并且你还想要在后台做其他事情时,会使用后台工作程序。在您的情况下,没有前台处理,您只需要每隔一段时间就可以运行一个作业。

此外,不要打扰睡眠业务等。而是使用调度程序框架(例如Quartz.NET)每隔一段时间唤醒它。这样,当您的服务启动时,它将初始化调度程序,并且在调度程序唤醒之前不会执行任何操作。当调度程序唤醒时,它会调用一个对象的方法,在初始化时它会告诉它调用它。

这样,服务在空闲时几乎不消耗任何CPU,并在需要时全力工作。

答案 6 :(得分:2)

Blog.StackOverflow.com有一篇关于使用缓存过期来处理周期性任务的有趣文章:

http://blog.stackoverflow.com/2008/07/easy-background-tasks-in-aspnet/

答案 7 :(得分:2)

有人对超级用户提出了类似的问题。您可以安装一个监视Windows服务的工具。类似Service Hawk之类的东西可以帮助您保持服务的启动,或者允许您安排自动重启(可能在夜间)以保持服务平稳运行。

答案 8 :(得分:1)

在我的公司,我们依靠Windows任务调度程序来启动服务,然后让服务自行关闭。这确保我们的服务始终在正确的时间运行,并且调度系统将允许报告成功/失败等。

答案 9 :(得分:1)

使用计时器执行此操作。

答案 10 :(得分:1)

我在1小时前和同事们进行了同样的讨论!我选择了while(_isPolling)选项,因为我需要同步进行工作。我不希望其他线程接收相同的工作(Timer方法),并且实现额外的锁定似乎是浪费。

答案 11 :(得分:0)

我应用了Timer和工作线程的组合,到目前为止它工作正常。

我将我的计时器设置为每分钟打勾,如果时间与计划时间匹配,我会创建我的工作线程。

我用这种技术做家务:

if (nowTimeVar.ToShortTimeString().CompareTo(houseKeep.ToShortTimeString()) == 0)
{
    myHouseKeeper = new clsHouseKeep(archiveFolder, lastday, myCounter, myLogger);                
}    

答案 12 :(得分:0)

在检查了很多教程后,我开发了每隔60分钟定期运行的Windows服务,它将在后台启动进程并运行Test.exe。

public partial class Scheduler : ServiceBase
{
    private Timer timer1 = null;


    public Scheduler()
    {
        InitializeComponent();
    }
    // Get the Exe path
    private string GetPath()
    {
        Assembly assembly = Assembly.GetExecutingAssembly();
        return Path.Combine(Path.GetDirectoryName(assembly.Location), "Test.exe");

    }
    // you can right your own custom code
    // Above vista version the process works as Administrator- "runas" and in old OS it will Start process as per Administartor rights  
    public void startprocess()
    {
       // System.Diagnostics.Process.Start(GetPath());
        System.Diagnostics.Process process = null;
        System.Diagnostics.ProcessStartInfo processStartInfo;

        processStartInfo = new System.Diagnostics.ProcessStartInfo();

        processStartInfo.FileName = GetPath();

        if (System.Environment.OSVersion.Version.Major >= 6)  // Windows Vista or higher
        {
            processStartInfo.Verb = "runas";
        }
        else
        {
            // No need to prompt to run as admin
        }

        processStartInfo.Arguments = "";

        process = System.Diagnostics.Process.Start(processStartInfo);

    }
    // On start method will load when system Start 
    // timer1_tick Event will call every hour after once start the service automatically
    protected override void OnStart(string[] args)
    {
        //System.Diagnostics.Debugger.Launch();
        timer1 = new Timer();
        this.timer1.Interval = 3600000; //every 60 min
        this.timer1.Elapsed += new System.Timers.ElapsedEventHandler(this.timer1_Tick);
        timer1.Enabled = true;

    }

    // Timer1_tick will Start process every hour
    private void timer1_Tick(object sender, ElapsedEventArgs e)
    {
        //Write code here to do some job depends on your requirement
        startprocess();
    }
    // Method will stop the service
    // we have set the method stop service as false. so service will not stop and run every hour 
    protected override void OnStop()
    {
        timer1.Enabled = false;

    }

}