C#中的三種timer

html

https://blog.csdn.net/hoiven/article/details/51362582安全

若是你須要使用規律的時間間隔重複執行一些方法,最簡單的方式是使用定時器(timer)。多線程

.NET Framework 提供了 4 種定時器。下邊兩個類是通用的多線程定時器:ide

(1)System.Threading.Timer
(2)System.Timers.Timer
另外兩個是專用的單線程定時器:函數

(3)System.Windows.Forms.Timer (Windows Forms 的定時器)
(4)System.Windows.Threading.DispatcherTimer (WPF 的定時器)
多線程定時器更增強大、精確而且更加靈活,而單線程定時器對於一些簡單的更新 Windows Forms 和 WPF 控件的任務來講是安全的,而且更加便捷。fetch

1.多線程定時器ui

System.Threading.Timer是最簡單的多線程定時器:它僅僅有一個構造方法和兩個普通方法(取悅於極簡主義者,還有本書做者!)。在接下來的例子中,一個定時器在 5 秒鐘以後調用Tick方法來打印 「 tick… 「,以後每秒打印一次直到用戶按下回車鍵:this

using System;
using System.Threading;
 
class Program
{
 static void Main()
 {
  // 首次間隔 5000ms,以後間隔 1000ms
  Timer tmr = new Timer (Tick, "tick...", 5000, 1000);
  Console.ReadLine();
  tmr.Dispose();     // 中止定時器並執行清理工做
 }
 
 static void Tick (object data)
 {
  // 這裏運行在一個線程池線程上
  Console.WriteLine (data);     // 打印 "tick..."
 }
}

以後能夠經過調用Change方法來改變定時器的時間間隔。若是你但願定時器只觸發一次,能夠指定Timeout.Infinite做爲構造方法的最後一個參數。spa

二、.NET Framework 在System.Timers命名空間下提供了另外一個名字相同的定時器類。它只是封裝了 System.Threading.Timer,並在使用徹底相同的底層引擎的前提下提供額外的便利。下面是增長功能的簡介:操作系統

(1)實現了Component,容許用於 Visual Studio 的設計器中。
(2)Interval屬性代替了Change方法。
(3)Elapsed事件代替了回調委託。
(4)Enabled屬性用於開始或中止定時器(默認值是false)。
(5)Start和Stop方法,避免對Enabled屬性感到困惑。
(6)AutoReset標識來指定是否爲可重複的事件(默認爲true)。
SynchronizingObject屬性提供Invoke和BeginInvoke方法,用於在 WPF 和 Windows Forms 控件上安全調用方法。

using System;
using System.Timers;  // 命名空間是 Timers 而不是 Threading
 
class SystemTimer
{
 static void Main()
 {
  Timer tmr = new Timer();    // 無需任何參數
  tmr.Interval = 500;
  tmr.Elapsed += tmr_Elapsed;  // 使用事件代替委託
  tmr.Start();          // 開啓定時器
  Console.ReadLine();
  tmr.Stop();          // 中止定時器
  Console.ReadLine();
  tmr.Start();          // 重啓定時器
  Console.ReadLine();
  tmr.Dispose();         // 永久中止定時器
 }
 
 static void tmr_Elapsed (object sender, EventArgs e)
 {
  Console.WriteLine ("Tick");
 }
}

多線程定時器使用線程池來容許少許線程服務多個定時器。這意味着,回調方法或Elapsed事件每次可能會在不一樣的線程上觸發。此外,不論以前的Elapsed是否完成執行,Elapsed老是幾乎按時觸發。所以,回調方法或事件處理器必須是線程安全的。

多線程定時器的精度依賴於操做系統,一般是在 10-20 ms 的區間。若是須要更高的精度,你可使用本地互操做(native interop)來調用 Windows 多媒體定時器,可讓精度提高到 1 ms。它定義在 winmm.dll 中,首先調用timeBeginPeriod來通知操做系統你須要更高的定時器精度,而後調用timeSetEvent來啓動多媒體定時器。當使用完成後,調用timeKillEvent中止定時器,最後調用timeEndPeriod通知操做系統你不在須要更高的定時器精度了。能夠經過搜索關鍵字 dllimport winmm.dll timesetevent 在網上找到完整的例子。

三、它們暴露的成員都像System.Timers.Timer同樣(Interval、Tick、Start和Stop),而且用法也相似。可是不一樣之處在於其內部是如何工做的。它們不是使用線程池來產生定時器事件,WPF 和 Windows Forms 定時器依賴於 UI 模型的底層消息循環機制(message pumping mechanism)。意味着Tick事件老是在建立該定時器的那個線程觸發,在一般的程序中,它也就是管理全部 UI 元素和控件的那個線程。

單線程計時器比較安全,對於更新 Windows Forms controls或者WPF這種簡單任務來講更方便。在WPF或Windows Forms中安全的調用方法的SynchronizingObject對象。
單線程計時器是被設計成屬於他們執行環境的計時器,若是你在一個Windows服務應用程序中使用Windows Forms的Timer,timer 事件並不會被觸發,只有在對應的環境下才會被觸發。
像System.Timers.Timer同樣,他們也提供了相同的成員(Interval,Tick,Start,Stop),可是他們內部的工做原理不一樣,WPF和Windows Forms的計時器使用消息循環機制來取代線程池產生消息的機制。

你能夠沒必要考慮線程安全。
新的Tick在以前的Tick完成執行前不會觸發。
你能夠直接在Tick時間事件的處理代碼中更新 UI 控件,而不須要調用Control.Invoke或Dispatcher.Invoke。
這聽起來好的難以置信,直到你意識到使用這些定時器的程序並非真正的多線程,不會有並行執行。一個線程服務於全部定時器,而且還處理 UI 事件。這帶來了單線程定時器的缺點:

除非Tick事件處理器執行的很快,不然 UI 會失去響應。
這使得 WPF 和 Windows Forms 定時器僅適用於小任務,一般就是那些更新 UI 外觀的任務(例如,顯示時鐘或倒計時)。不然,你就須要多線程定時器。

在精度方面,單線程定時器與多線程定時器相似(幾十毫秒),可是一般精度更低,由於它們會被其它 UI 請求(或其它定時器事件)推遲。

單線程計時器基於Windows消息循環,應用程序會同步的處理計時器的消息。會發現UI界面相應速度比較慢。解決這個問題的方法是使用多線程計時器。
單線程計時器的缺點:除非Tick事件的處理代碼執行的很是快,不然UI界面會變得響應很慢。因此 WPF和Windows Forms的計時器都很是適合小任務,尤爲是界面更新的任務。例如時鐘和計數顯示。不然,你須要一個多線程計時器

用法舉例

System.Timers.Timer是多線程定時器,若是一個Timer沒有處理完成,到達下一個時間點,新的Timer一樣會被啓動,因此在使用Timer時須要注意,當設定的時間間隔遠大於任務時間時,可以實現只在一個線程中執行,可是不能確保必定是一個。

https://blog.csdn.net/yl2isoft/article/details/51346469

如何prevent System.Timers.Timer的觸發事件從一個線程池排隊來執行?(How to prevent System.Timers.Timer from queuing for execution on a thread pool?)

There is a problem with standard System.Timers.Timer behaviour. The timer raise Elapsed event with some interval. But when time of execution inside Elapsed event handler exceed timer interval then thread pool begin queuing event handling. This is a problem in my case. This is because with my Elapsed event handler I fetch some data from database and doing something with it and finally save results back to database. But data handling should be provided only once. So, is there a way to prevent from queuing elapse events for System.Timers.Timer.(怎樣只讓事件執行一次)

As illustration for this issue you can consider next test program:

public class EntryPoint
{

    private static void TimeProc(object state, ElapsedEventArgs e)
    {
        Console.WriteLine("Current time {0} on the thread {1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(20000);
    }

    static void Main(string[] args)
    {
        Console.WriteLine("Press <Enter> for finishing\n\n");
        ThreadPool.SetMaxThreads(10, 10);
        System.Timers.Timer MyTimer = new System.Timers.Timer(1000);
        MyTimer.Elapsed += new ElapsedEventHandler(TimeProc);
        MyTimer.Start();
        Console.ReadLine();
        MyTimer.Stop();
    }
}

解決方法:

public class EntryPoint
    {
        private static System.Timers.Timer MyTimer;
        private static void TimeProc(object state, ElapsedEventArgs e)
        {
            Console.WriteLine("Current time {0} on the thread {1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(20000);
           MyTimer.Enabled = true;
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Press <Enter> for finishing\n\n");
            ThreadPool.SetMaxThreads(10, 10);
            MyTimer = new System.Timers.Timer(1000);
           MyTimer.AutoReset = false;

            MyTimer.Elapsed += new ElapsedEventHandler(TimeProc);
          MyTimer.Enabled = true;
            Console.ReadLine();

        }
    }

以上方法彷佛不行,不知道有什麼可以確保只在一個線程中執行觸發函數的方法。

http://www.javashuo.com/article/p-wijtbufu-gc.html

http://www.javashuo.com/article/p-nzotwccc-ez.html

相關文章
相關標籤/搜索