定时任务的处理

      对于定时任务,一般由时间戳Timers 或者死循环While(true) 操作,两者都能达到,指定间隔时间内执行去执行任务,这里不做效率的比较,只说明一下适合的场景。先拿while来说,执行完成任务后,设置好线程等待时间即可,它有一优势,即如果此次任务未执行完,则不会进入下一次,也就是说是可控的,不会同时执行两笔相同任务

        public void StartLoadTask()
        {
            while (true)
            {
                //... 业务逻辑//等待60秒,进入下一次
                Thread.Sleep(60000);
            }
        }

再来看一看Times ,这里仅作 System.Timers 下的timer的说明(非 Threading 中的timer),如下图

 //执行定时服务  
 var timer = new Timer() { AutoReset = true, Enabled = true, Interval=60000 };
 timer.Elapsed += (sender, eventArgs) => Helper.StartTask();

可以满足每隔60s 调用一次 StartTask 方法,但是会有一个问题,试想如果由于某个内部或外部原因,导致执行一次程序的时间超过了60s,如果直接这样写,则会出现第一个任务还未执行完,下一个任务又进来,则同时运行两笔任务的情况,显然是不可取的,需要进行改进,即需等上次任务执行完,再去等待间隔,然后进入下一次任务,否则永远要等上一笔任务执行完。这里将 timer 当做参数 传递给 StartTask 方法,则

        public void StartTask(Timer timer)
        {
            timer.Stop();            
            //... 业务逻辑
            timer.Start();

        }

看似两种操作都达到了需求,细想下来,还有不妥。试想任务是每分钟执行一次的,虽然这样处理解决了不会同时执行多笔任务的问题,可若任务执行时间超过了60s,则错过了下一次任务的执行时间。这样的话岂不是顾此失彼,既要每次只执行一笔任务,又不能错过每一次任务。想要满足这种要求,则只能引用安全队列机制,每隔60s,便把任务写入队列中,再启一个线程去循环读取队列里的任务便可。这样一个线程尽管往里面写,另外一个线程负责取,每执行完一笔任务,就去读下一笔任务,如此往复。

写入队列如下

        public void StartTask()
        {
            //任务t
            //写入任务队列              
            TaskQueue.Enqueue(t);
            LogHelper.LogQueue.Enqueue("写入报告任务:" + t.Date);
        }

取出队列如下

        public void StartTask()
        {
            while (true)
            {
                if (!TaskQueue.IsEmpty)
                {
                    //取出任务
                    TaskQueue.TryDequeue(out ICCTask task);
                    LogHelper.LogQueue.Enqueue("取出报告任务:" + task.Date);
                    //执行任务 逻辑
                    // ...
                }
                Thread.Sleep(60000);
            }
        }

当然取出队列任务 需要设置线程为 异步  Task.Factory.StartNew(StartTask);

 

posted @ 2019-12-09 16:55  郎中令  阅读(513)  评论(0编辑  收藏  举报