2010-07-14 33 views
57

我在SO上搜索並找到關於Quartz.net的答案。但對我的項目來說似乎太大了。我想要一個等效的解決方案,但更簡單和(最好)代碼(不需要外部庫)。我怎樣才能每天在特定時間調用一種方法?如何在C#中的特定時間每天調用一個方法?

我需要補充一些有關此信息:

  • 做到這一點最簡單的(醜陋的)方式,是檢查的時間每一秒/分鐘,調用該方法,在適當的時候

我想要一個更有效的方法來做到這一點,不需要經常檢查時間,而且我可以控制工作是否完成了。如果方法失敗(由於任何問題),程序應該知道寫入日誌/發送電子郵件。這就是爲什麼我需要調用方法,而不是安排工作。

我在Java中找到了這個解決方案Call a method at fixed time in Java。在C#中有類似的方法嗎?

編輯:我已經這樣做了。我在void Main()中添加了一個參數,並創建了一個bat(由Windows任務計劃程序調度)以使用此參數運行程序。程序運行,完成工作,然後退出。如果一項工作失敗,它可以寫日誌和發送電子郵件。這種方法適合我的要求以及:)

+2

該鏈接的問題似乎表明,您的運行應用程序中的方法必須定期調用。是這樣嗎?它會影響您是否需要進行中調度或是否只能使用Windows調度程序。 – paxdiablo 2010-07-14 04:21:57

+0

我的程序會根據需求連續運行 – Vimvq1987 2010-07-14 04:24:25

+0

嘿,我不敢相信你稱我的答案「醜陋」。他們的鬥爭'的話:-) – paxdiablo 2010-07-14 04:39:13

回答

67
  • 創建一個控制檯應用程序已經做了你在找什麼
  • 使用Windows「Scheduled Tasks」功能,必須在你需要它運行時執行的控制檯應用程序

這真是你所需要的!

更新:,如果你想這樣做你的應用程序裏面,你有幾種選擇:

    的Windows
  • 窗體的應用程序,你可以挖掘到Application.Idle事件和檢查,看看是否你現在已經到了這個時候打電話給你的方法。此方法僅在您的應用程序不忙於其他內容時調用。一個快速檢查,看看你的目標時間已經達到不應該把太多的壓力,你的應用程序,我認爲...
  • 在ASP.NET的Web應用程序,有「模擬」發送預定事件 - 看看這個CodeProject article
  • ,當然,你也可以只是簡單的在任何.NET應用程序「滾你自己」 - 看看這個CodeProject article對樣品實施

更新#2:如果您想要每60分鐘檢查一次,您可以創建一個每60分鐘醒來一次的計時器,如果時間到了,它會調用該方法。

事情是這樣的:

using System.Timers; 

const double interval60Minutes = 60 * 60 * 1000; // milliseconds to one hour 

Timer checkForTime = new Timer(interval60Minutes); 
checkForTime.Elapsed += new ElapsedEventHandler(checkForTime_Elapsed); 
checkForTime.Enabled = true; 

,然後在你的事件處理程序:

void checkForTime_Elapsed(object sender, ElapsedEventArgs e) 
{ 
    if (timeIsReady()) 
    { 
     SendEmail(); 
    } 
} 
+0

以前想過。 :)。但我的程序會持續運行,並且我想知道另一種方式,如果有:) – Vimvq1987 2010-07-14 04:23:02

+0

這是一個Winform應用程序。我會盡力說服老闆改變設計,但起初我應該儘量滿足他的要求:p – Vimvq1987 2010-07-14 04:25:35

+0

謝謝你的回答。我添加了一些信息。我很感激,如果你能幫助... – Vimvq1987 2010-07-14 04:37:27

4

,我知道的,也許最簡單的就是使用Windows任務計劃程序來執行最好的方法你在一天中的特定時間執行代碼或讓應用程序永久運行,並檢查特定時間,或編寫相同的Windows服務。

1

如果您想要運行可執行文件,請使用Windows計劃任務。我將假設(也許是錯誤的)你想要一個方法在當前程序中運行。

爲什麼不只是有一個線程持續運行,存儲方法被調用的最後日期?

是否每分鐘喚醒一次(例如),如果當前時間大於指定時間並且存儲的最後日期不是當前日期,則調用該方法,然後更新日期。

8

正如其他人所說的,您可以使用控制檯應用程序在計劃時運行。別人沒有說的是,你可以通過這個應用程序觸發你在主應用程序中等待的跨進程EventWaitHandle。

控制檯應用程序:

class Program 
{ 
    static void Main(string[] args) 
    { 
     EventWaitHandle handle = 
      new EventWaitHandle(true, EventResetMode.ManualReset, "GoodMutexName"); 
     handle.Set(); 
    } 
} 

主要應用:

private void Form1_Load(object sender, EventArgs e) 
{ 
    // Background thread, will die with application 
    ThreadPool.QueueUserWorkItem((dumby) => EmailWait()); 
} 

private void EmailWait() 
{ 
    EventWaitHandle handle = 
     new EventWaitHandle(false, EventResetMode.ManualReset, "GoodMutexName"); 

    while (true) 
    { 
     handle.WaitOne(); 

     SendEmail(); 

     handle.Reset(); 
    } 
} 
8

每當我建立需要這種功能的應用,我總是通過簡單的.NET使用Windows任務計劃程序圖書館,我發現。

請對see my answer to a similar question瞭解一些示例代碼和更多解釋。

4

我知道這是舊的,但這個怎麼樣:

建立一個定時器火在啓動時計算時間,下次運行時間。在運行時的第一個調用中,取消第一個定時器並啓動一個新的每日定時器。每天更改爲小時或任何你想要的週期性。

+8

並且在夏時制時間更改期間觀察它失敗。 。 。 – 2013-03-14 20:49:00

+1

好點,雖然可以得到補償。 – Brent 2014-03-12 14:14:56

0

您可以計算剩餘時間並將計時器設置爲這個的一半(或其他分數),而不是設置每60分鐘每秒運行一次的時間。這樣,您不會檢查時間,而是可以在計時器間隔時間內確定一定程度的累計時間,從而縮短與目標時間的距離。

例如,如果你想要做一些事情,從現在60分鐘計時器的時間間隔將aproximatly:

30:00:00,15:00:00,七時30分00秒,3時45分00秒,...,00:00:01,RUN!

我使用下面的代碼每天自動重新啓動一次服務。我使用了一個線程,因爲我發現定時器在很長一段時間內不可靠,而在這個例子中這是比較昂貴的,它是唯一爲此目的而創建的,所以這無關緊要。

(從VB.NET轉換)

autoRestartThread = new System.Threading.Thread(autoRestartThreadRun); 
autoRestartThread.Start(); 

...

private void autoRestartThreadRun() 
{ 
    try { 
     DateTime nextRestart = DateAndTime.Today.Add(CurrentSettings.AutoRestartTime); 
     if (nextRestart < DateAndTime.Now) { 
      nextRestart = nextRestart.AddDays(1); 
     } 

     while (true) { 
      if (nextRestart < DateAndTime.Now) { 
       LogInfo("Auto Restarting Service"); 
       Process p = new Process(); 
       p.StartInfo.FileName = "cmd.exe"; 
       p.StartInfo.Arguments = string.Format("/C net stop {0} && net start {0}", "\"My Service Name\""); 
       p.StartInfo.LoadUserProfile = false; 
       p.StartInfo.UseShellExecute = false; 
       p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; 
       p.StartInfo.CreateNoWindow = true; 
       p.Start(); 
      } else { 
       dynamic sleepMs = Convert.ToInt32(Math.Max(1000, nextRestart.Subtract(DateAndTime.Now).TotalMilliseconds/2)); 
       System.Threading.Thread.Sleep(sleepMs); 
      } 
     } 
    } catch (ThreadAbortException taex) { 
    } catch (Exception ex) { 
     LogError(ex); 
    } 
} 

注我已經設置的1000毫秒的間隔的最小值,這可以increaded,減少或去除,這取決於滿足你的要求。

記得在應用程序關閉時也停止線程/計時器。

1

我剛剛寫了一個不得不每天重新啓動的c#應用程序。我意識到這個問題很老,但我不認爲它會增加另一種可能的解決方案。這是我在特定時間處理每日重啓的方式。

public void RestartApp() 
{ 
    AppRestart = AppRestart.AddHours(5); 
    AppRestart = AppRestart.AddMinutes(30); 
    DateTime current = DateTime.Now; 
    if (current > AppRestart) { AppRestart = AppRestart.AddDays(1); } 

    TimeSpan UntilRestart = AppRestart - current; 
    int MSUntilRestart = Convert.ToInt32(UntilRestart.TotalMilliseconds); 

    tmrRestart.Interval = MSUntilRestart; 
    tmrRestart.Elapsed += tmrRestart_Elapsed; 
    tmrRestart.Start(); 
} 

爲了確保您的計時器保持在範圍上,我建議使用System.Timers.Timer tmrRestart = new System.Timers.Timer()方法,該方法之外創建它。將方法RestartApp()放入您的表單加載事件中。應用程序啓動時,將設置值AppRestart如果current比重啓時間時,我們增加1天AppRestart,以確保重新啓動發生在時間和我們沒有得到一個例外放一個負值定時器。在tmrRestart_Elapsed事件中,運行您需要的任何代碼在該特定時間運行。如果在它自己的應用程序重新啓動你不一定要停止計時,但它也沒有破壞,如果應用程序沒有重新啓動只需再次調用RestartApp()方法,你會好到哪裏去。

-1

這可能只是我,但它似乎像大多數的答案都是不徹底或將無法正常工作。我做了一件非常快速和骯髒的事。這是說不知道這樣做的想法是多麼好,但它每次都是完美的。

while (true) 
{ 
    if(DateTime.Now.ToString("HH:mm") == "22:00") 
    { 
     //do something here 
     //ExecuteFunctionTask(); 
     //Make sure it doesn't execute twice by pausing 61 seconds. So that the time is past 2200 to 2201 
     Thread.Sleep(61000); 
    } 

    Thread.Sleep(10000); 
} 
0

我對此有一個簡單的方法。這會在動作發生之前創建1分鐘的延遲。你也可以添加秒來創建Thread.Sleep();短。

private void DoSomething(int aHour, int aMinute) 
{ 
    bool running = true; 
    while (running) 
    { 
     Thread.Sleep(1); 
     if (DateTime.Now.Hour == aHour && DateTime.Now.Minute == aMinute) 
     { 
      Thread.Sleep(60 * 1000); //Wait a minute to make the if-statement false 
      //Do Stuff 
     } 
    } 
} 
2

我創建了一個易於使用的簡單調度程序,不需要使用外部庫。 TaskScheduler是一個單引擎,它保持對定時器的引用,因此定時器不會被垃圾收集,它可以安排多個任務。您可以設置在第一次運行(小時和分鐘),如果在安排這一次的時間超過在當時的第二天這個計劃的開始。但是定製代碼很容易。

安排新的任務是如此簡單。例如:在11:52,第一個任務是每15個分支,第二個分支是每個5個分支。對於日常執行,將24設置爲3參數。

TaskScheduler.Instance.ScheduleTask(11, 52, 0.00417, 
    () => 
    { 
     Debug.WriteLine("task1: " + DateTime.Now); 
     //here write the code that you want to schedule 
    }); 

TaskScheduler.Instance.ScheduleTask(11, 52, 0.00139, 
    () => 
    { 
     Debug.WriteLine("task2: " + DateTime.Now); 
     //here write the code that you want to schedule 
    }); 

我的調試窗口:

task2: 07.06.2017 11:52:00 
task1: 07.06.2017 11:52:00 
task2: 07.06.2017 11:52:05 
task2: 07.06.2017 11:52:10 
task1: 07.06.2017 11:52:15 
task2: 07.06.2017 11:52:15 
task2: 07.06.2017 11:52:20 
task2: 07.06.2017 11:52:25 
... 

只需將該類添加到您的項目:

public class TaskScheduler 
{ 
    private static TaskScheduler _instance; 
    private List<Timer> timers = new List<Timer>(); 

    private TaskScheduler() { } 

    public static TaskScheduler Instance => _instance ?? (_instance = new TaskScheduler()); 

    public void ScheduleTask(int hour, int min, double intervalInHour, Action task) 
    { 
     DateTime now = DateTime.Now; 
     DateTime firstRun = new DateTime(now.Year, now.Month, now.Day, hour, min, 0, 0); 
     if (now > firstRun) 
     { 
      firstRun = firstRun.AddDays(1); 
     } 

     TimeSpan timeToGo = firstRun - now; 
     if (timeToGo <= TimeSpan.Zero) 
     { 
      timeToGo = TimeSpan.Zero; 
     } 

     var timer = new Timer(x => 
     { 
      task.Invoke(); 
     }, null, timeToGo, TimeSpan.FromHours(intervalInHour)); 

     timers.Add(timer); 
    } 
} 
0

這裏有一個辦法做到這一點使用TPL。無需創建/處理定時器等:

void ScheduleSomething() 
{ 

    var runAt = DateTime.Today + TimeSpan.FromHours(16); 

    if (runAt <= DateTime.Now) 
    { 
     DoSomething(); 
    } 
    else 
    { 
     var delay = runAt - DateTime.Now; 
     System.Threading.Tasks.Task.Delay(delay).ContinueWith(_ => DoSomething()); 
    } 

} 

void DoSomething() 
{ 
    // do somethig 
} 
相關問題