主頁 > .NET開發 > 實作一個“計劃任務”機制

實作一個“計劃任務”機制

2020-10-11 23:39:09 .NET開發

概述

最近接到一個任務 要做一個《計劃任務》的東西,簡而言之的說 就是事先設定好時間 定期執行指定代碼的功能 我們這個很簡單 就是每天或者每幾天 那天的一個固定時間比如23:20執行一段固定代碼,好,看一個界面

 

 

是不是很熟悉 哇哈哈哈,類似 Windows自帶的 計劃任務功能, 對就是這個,按說的話微軟自家的東西 肯定有對應的介面或者 方便銜接的東西,于是網上找來找去 ,最終的結果還是沒有采用他,一個原因是Windows自帶的計劃任務功能太復雜 我根本用不了那么多,第二個原因是沒有找到一個讓我舒心的呼叫方式,期間也找過其它的第三方的東西比如quartz 是很牛逼 看了下也是扯淡 大部頭的東西 又是要安裝這安裝那的 不適合我的簡易需求環境,

實作原理

后來靜下心來仔細想了下 憑什么到指定時間執行指定代碼 ,還不是程式開始的時候根據計劃任務的時間進行了一個計算 然后設定一個timer讓代碼到期執行嗎,難道還有什么其他歪可以找嗎?搞清楚了事情的本質接下來就好辦了,就像期間找過一個mysql不能登錄的問題網上只說啥啥啥方式在命令列輸入 仔細細看一下那個圖上的陳述句明顯是一個mysql的控制臺并不一定要按照他說的流程打開 有可能我沒添加環境變數各種原因打不開 我直接運行mysql控制臺就好了, 所有做事情做不通前要想一下為很么 會好辦的多,計劃任務本來不就是算過期時間嗎?什么你懷疑timer不穩定?不用timer用什么?按說時間計算是作業系統自暴露的一個API最基本的一個功能,就像檔案讀取 就那樣了對于我們搞應用開發的 已經沒有更底層的可以挖了,就算你拿C++來 我相信還是只有用類似timer的這種玩意兒 不相信還有其他歪可以找,timer不穩定 不要使用winform界面的那個timer 那玩意兒估計確實不穩定 命名空間下有好幾個同名的東西,其實程序無法就是程式啟動的時候計算過期時間 按從設定之處開始算 接下來什么時候執行 ,一個timer 加計劃任務資料 經過精密的邏輯編程即可解決所有問題 不用去搞任何第三方的東西 ,并且我還有可控的 銜接友好的 界面 進行 任務管理,

實作

其中最主要的是構建一個ScheduleItemObj物件,里面有任務開始時間 間隔天數 任務的當天執行時間 ,相信聰明如你 ,最重要的就是 里面有一個timer 然后 一個委托 委托里面是你要執行的代碼,然后就是初始化方法 初始化的時候進行邏輯計算 確定下次執行時間 設定timer ,當timer往后每次執行的時候自動計算下次時間 然后設定timer ,如此往復即可,多個任務放一個list里 通過propertyChange 進行界面更新,與管理,整個程式的結構大概就是這樣了,我們使用的是System.Timers.Timer

以下是實作代碼:注意最重要的一段代碼是計算下次任務執行時間邏輯的處理,執行時間的機制:從創建的當天 的指定時間 ,每過指定單位量時間執行計劃任務,比如每天10:10:10 執行,如果創建的時候是09:00:00 則當天的 10:10:10 首次執行 往后以此類推,如果創建的時候是11:00:00 則在下一天的 10:10:10 首次執行 往后以此類推,配置一次就可以了 只要程式運行著 計劃任務就會在指定日期,不論是中途手動退出程式重新開,計算機重啟,都無需干預 ,會自動按照計劃任務串列里來執行,執行完后會自動重繪下次執行時間 和歷史執行記錄,代碼的注釋把以上原理闡述的很清楚,

void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                if (Started == false)//首次添加任務 或者程式剛啟動 安排接下來執行時間
                {
                    //進行首次的當天執行
                    DateTime now = DateTime.Now;

                    DateTime actionTime;
                    DateTimeFormatInfo dtfi = new CultureInfo("zh-CN", false).DateTimeFormat;
                    bool convertok = DateTime.TryParseExact(Time, "HH:mm:ss", dtfi, DateTimeStyles.None, out actionTime);
                    if (convertok == false)
                        return;

                    //年月日替換為 創建之日的 //開始時間必須從創建之時 開始算               

                    switch (DaysUnit)
                    {
                        case "天"://如果躍遷為天 替換掉 從當天的天開始算(時分秒 以計劃設定為準)
                            actionTime = new DateTime(CreateAt.Year, CreateAt.Month, CreateAt.Day, actionTime.Hour, actionTime.Minute, actionTime.Second);
                            break;

                        case "時"://如果躍遷為時 替換掉 從當天的天.時開始算(分秒 以計劃設定為準)
                            actionTime = new DateTime(CreateAt.Year, CreateAt.Month, CreateAt.Day, CreateAt.Hour, actionTime.Minute, actionTime.Second);
                            break;

                        case "分"://如果躍遷為分 替換掉 從當天的天.時.分開始算(秒 以計劃設定為準)
                            actionTime = new DateTime(CreateAt.Year, CreateAt.Month, CreateAt.Day, CreateAt.Hour, CreateAt.Minute, actionTime.Second);
                            break;
                        default:
                            break;
                    }


                    double interval = (actionTime - now).TotalMilliseconds;
                    if ((actionTime - now).TotalMilliseconds <= 0)//計劃時間在當前時間以前
                    {
                        //設定下次執行時間                       

                        NextActionAt = actionTime;



                        while (interval <= 0)
                        {

                            //如果很久沒有啟動程式了 加了躍遷 都還是在歷史日期

                            //一直加日期 直到加到超過當前日期
                            switch (DaysUnit)
                            {
                                case "天":
                                    actionTime = actionTime.AddDays(Days);
                                    break;

                                case "時":
                                    actionTime = actionTime.AddHours(Days);
                                    break;

                                case "分":
                                    actionTime = actionTime.AddMinutes(Days);
                                    break;
                                default:
                                    break;
                            }

                            interval = (actionTime - now).TotalMilliseconds;
                        }
                    }

                    //此處必定已經累加到interval躍遷大于0了
                    //Console.WriteLine("aaa");
                    if (interval > 0)
                    {
                        NextActionAt = actionTime;

                        timer.Interval = interval;
                        timer.AutoReset = false;
                        timer.Start();


                        Console.WriteLine(ID + "初始化");

                        Started = true;
                    }

                }
                else//運行中
                {
                    DateTime now = DateTime.Now;
                    Console.WriteLine(ID + "執行于:" + NextActionAt.ToString());


                    try
                    {

                        //串口操作
                        ComDevice.Open();
                        byte[] data1 = new byte[] { 0xAA };
                        byte[] data2 = new byte[] { 0xBB };
                        byte[] data3 = new byte[] { 0xCC };
                        
                        ComDevice.Write(data1, 0, 1);
                        Thread.Sleep(Spand1 * 1000);
                        ComDevice.Write(data2, 0, 1);
                        Thread.Sleep(Spand2 * 1000);
                        ComDevice.Write(data3, 0, 1);
                        ComDevice.Close();

                        //操作完成 更新資料庫記錄 何下次action時間  ,并且設定自身nextActionAt

                        if (historyAdd != null)
                        {
                            History hist = new History();
                            hist.Success = 1;
                            hist.SuccessStr = "成功";
                            hist.ScheduleID = ID;
                            hist.ActionAt = NextActionAt.Value;
                            historyAdd.Invoke(hist);
                        }
                    }
                    catch (Exception ex)
                    {
                        LoggerManager.Instance.WriteLog(ex.Message);
                        if (historyAdd != null)
                        {
                            History hist = new History();
                            hist.Success = 0;
                            hist.SuccessStr = "失敗";
                            hist.ScheduleID = ID;
                            hist.ActionAt = NextActionAt.Value;
                            historyAdd.Invoke(hist);
                        }
                    }
                    finally
                    {
                        //無論如何都進行下次的計劃任務定制
                        //執行到此處的時候肯定是actionAt時間到了,只需再加上days即可
                        switch (DaysUnit)
                        {
                            case "天":
                                NextActionAt = NextActionAt.Value.AddDays(Days);
                                break;

                            case "時":
                                NextActionAt = NextActionAt.Value.AddHours(Days);
                                break;

                            case "分":
                                NextActionAt = NextActionAt.Value.AddMinutes(Days);
                                break;
                            default:
                                break;
                        }


                        timer.Interval = (NextActionAt.Value - now).TotalMilliseconds;
                        timer.AutoReset = false;
                        timer.Start();
                    }


                }
            }
            catch (Exception ex)
            {

                Console.WriteLine("遇到錯誤:" + ex.Message);
                LoggerManager.Instance.WriteLog(ex.Message);
            }

        }

在程式啟動的時候從access資料庫讀取記錄 然后把所有計劃任務都計算并啟動一遍,

public void StartSchedule()
{
    //清理以前的
    if (RunningSchedule != null)
    {
        for (int i = 0; i < RunningSchedule.Count; i++)
        {
            RunningSchedule[i].TimerClose();
        }
        RunningSchedule.Clear();
    }
    else
    {
        RunningSchedule = new ObservableCollection<ScheduleItemObj>();
    }


    if (Data =https://www.cnblogs.com/assassinx/archive/2020/10/11/= null || Data.Count == 0)
    {
        return;
    }

    for (int i = 0; i < Data.Count; i++)
    {
        RunningSchedule.Add(Data[i]);
        Data[i].historyAdd += new Action((hist) => {

            uiDispatcher.Invoke(new Action(() => { 
            
            this.AddDBHistory(hist);
            this.AddHistoryVirtual(hist);
            }));
        });
        Data[i].Start();
    }
}

 

當然 如果是添加新任務 我們也是很簡易粗暴的 添加資料 然后把所有任務停止,停止的時候會回收資源, 然后再啟動一遍 ,這樣便于我們更簡易的控制,

if (sw.ShowDialog() == true)
{
    vm.AddSchedule(sw.day, sw.time.ToString("HH:mm:ss"),sw.daysUnit);
    vm.StartSchedule();
}

然后我們做了一個啟動時自動縮小到任務欄托盤運行的方式,我們是根據開始基礎日期 的設定進行 躍遷 單位 計算下次執行時間的 而不是累加,所以程式運行多久都不會出現執行時間上的偏差,

當然 有幾個東西要知曉 ,1應用程式必須要一直在運行期間計劃任務才能夠得到成功執行,系統沒有登錄的情況下不會得到執行,我的運用環境是滿足的, 要規避這個問題網上說可以弄成服務形式的,

好了完工,上一個運行圖,好,完美,此程式現在已穩定運行相當長一段時間了,未出過問題,

 

 

 

 

 

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/168184.html

標籤:.NET技术

上一篇:力扣初級演算法(二)【字串】

下一篇:自定義檔案型別用一程式打開,只能打開一個程式實體,怎么實作?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more