• 使用C#实现计划任务(corn job)


    维基百科上是这样描述计划任务的:

    “Cron is a time-based job scheduler in Unix-like computer operating systems. Cron is short for Chronograph. Cron enables users to schedule jobs (commands or shell scripts) to run automatically at a certain time or date.”

    有些场景下你需要定时的重复调度某些操作,下面是一些定时的例子:

    • 每15分钟
    • 每0,15,30,45 分钟
    • 每天12 点
    • 每天的12AM, 6AM, 12PM, 6PM
    • 星期六的 12PM
    • 每个月的第一天

    在这片文章里,我将会介绍使用C#实现计划任务。

    前人做的工作

    当我在工作中遇到这样的需求时,我的第一想法就是去网上看看,不是有句话嘛,不要重复发明轮子。根据网上的资料显示很多人已经在C#实现计划任务上做了很多工作。但是多数不符合我的要求,还有一部分实现的比较复杂。我就想做一个干干净净、清清爽爽的库,自己用。下面是我参考比较多的两个库。

    • Quartz.NET – 这个库比较全,其实他是一个线程调度库,比我要求的要全得多。
    • NCrontab – 这个库比较简单,实现的是一个工作调度器,缺少解析cron的部分,不适合我使用。

    我在NCrontab的基础上增加了cron表达式解析功能,使用事件触发完成预设工作的执行,完成了我的计划任务库。

    如何使用我的代码库?

    下面举一个例子来说明如何使用我的代码库完成建立一个计划任务:

    // Create the cron schedule.
    //
    CronExpression cronExpression = CronBuilder.CreateHourlyTrigger(new int[] {0, 30});
    CronSchedule cronSchedule = CronSchedule.Parse(cronExpression.ToString());
    List<CronSchedule> cronSchedules = new List<CronSchedule> { cronSchedule };
    
    // Create the data context for the cron object.
    //
    CronObjectDataContext dc = new CronObjectDataContext
    {
        Object = myObject,
        CronSchedules = cronSchedules,
        LastTrigger = DateTime.MinValue
    };
    
    // Create the cron object.
    //
    CronObject cron = new CronObject(dc);
    
    // Register for events.
    //
    cron.OnCronTrigger += Cron_OnCronTrigger;
    
    // Start the cron job.
    //
    cron.Start();

    首先建立一个调度器,在每小时的0分钟和30分钟触发事件,事件响应函数里面可以做多项工作。.  ‘CronBuilder’ 类可以辅助你建立计划任务,你也可以使用cron表达式完成计划任务的建立,上面例子中的任务可以使用 “0,30 * * * *” 或者 “*/30 * * * *”表示,相比之下使用CronBuilder使得工作更简单。

    当计划任务启动后,到了指定时间‘OnCronTrigger’事件将会触发,事件响应函数将会执行:

    private void Cron_OnCronTrigger(CronObject cronObject)
    {
        IRunnable myObject = cronObject.Object as IRunnable;
        myObject.Run();
    }

    cronObject类表示计划工作,其中包含可执行的方法。

    调度器Scheduler

    ‘CronSchedule’ 类表示调度器,其Parse方法解析计划任务表达式。这个类有一个私有构造函数,使用重载的Parse方法可以实例化一个新的实例。

    其中调度的算法是首先计算出计划时间与当前时间的间距,然后在当前一分钟内自旋等待,然后检查是否到达触发时刻,如果没有将已经自旋时间增加一分钟,继续下一分钟自旋,自旋满一个小时后,增加已经自旋的小时数,如此递增,自旋时间等于已经算出的时间间距。

    基于事件的任务触发

    ‘CronObjectDataContext 类包含三个属性.

    • CronSchedules – 一个事件可以包含多个调度器,也即是可以有多重触发机制.
    • LastTrigger – 这里记录了上次触发的时间.
    • Object – 此属性表示将要执行的任务.

    ‘CronObject’类包含两个公开方法:  Start 和 Stop。用来启动和停止任务触发:

    public bool Start()
    {
        lock (_startStopLock)
        {
            // Can't start if already started.
            //
            if (_isStarted)
            {
                return false;
            }
            _isStarted = true;
            _isStopRequested = false;
    
            // This is a long running process. Need to run on a thread
            //    outside the thread pool.
            //
            _thread = new Thread(ThreadRoutine);
            _thread.Start();
        }
    
        // Raise the started event.
        //
        if(OnStarted != null)
        {
            OnStarted(this);
        }
    
        return true;
    }
    
    public bool Stop()
    {
        lock (_startStopLock)
        {
            // Can't stop if not started.
            //
            if (!_isStarted)
            {
                return false;
            }
            _isStarted = false;
            _isStopRequested = true;
    
            // Signal the thread to wake up early
            //
            _wh.Set();
    
            // Wait for the thread to join.
            //
            if(!_thread.Join(5000))
            {
                _thread.Abort();
    
                // Raise the thread abort event.
                //
                if(OnThreadAbort != null)
                {
                    OnThreadAbort(this);
                }
            }
        }
    
        // Raise the stopped event.
        //
        if(OnStopped != null)
        {
            OnStopped(this);
        }
        return true;
    }.

    线程routine主要负责触发OnCronTrigger事件,其中使用EventWaitHandle来实现在下一个调度触发来临前线程等待。在Stop方法中也是使用这种机制来保证所有调度器全部停止的。

    private readonly EventWaitHandle _wh = new AutoResetEvent(false);
    private void ThreadRoutine()
    {
        // Continue until stop is requested.
        //
        while(!_isStopRequested)
        {
            // Determine the next cron trigger
            //
            DetermineNextCronTrigger(out _nextCronTrigger);
    
            TimeSpan sleepSpan = _nextCronTrigger - DateTime.Now;
            if(sleepSpan.TotalMilliseconds < 0)
            {
                // Next trigger is in the past. Trigger the right away.
                //
                sleepSpan = new TimeSpan(0, 0, 0, 0, 50);
            }
    
            // Wait here for the timespan or until I am triggered
            //    to wake up.
            //
            if(!_wh.WaitOne(sleepSpan))
            {
                // Timespan is up...raise the trigger event
                //
                if(OnCronTrigger != null)
                {
                    OnCronTrigger(this);
                }
    
                // Update the last trigger time.
                //
                _cronObjectDataContext.LastTrigger = DateTime.Now;
            }
        }
    }

    DetermineNextCronTrigger方法决定哪个调度器将被拿出来负责调度。

    private void DetermineNextCronTrigger(out DateTime nextTrigger)
    {
        nextTrigger = DateTime.MaxValue;
        foreach (CronSchedule cronSchedule in _cronObjectDataContext.CronSchedules)
        {
            DateTime thisTrigger;
            if(cronSchedule.GetNext(LastTigger, out thisTrigger))
            {
                if (thisTrigger < nextTrigger)
                {
                    nextTrigger = thisTrigger;
                }
            }
        }
    }

    累死我了,终于说完了。大家可是试用一下。

    猛击下载链接

    转载请注明:源自极客飞逸(http://www.fiiii.com

  • 相关阅读:
    内存映射
    docstring show under decorator
    eventlet dbpool for postgresql &mysql
    za python
    Install MySQL 5.0 Max on FC3
    vi
    ff chrome tips
    20101004网站部署更新
    sqlalchemy type &elixir type
    20100930网站部署更新日志
  • 原文地址:https://www.cnblogs.com/njuxdj/p/csharp_cron_task.html
Copyright © 2020-2023  润新知