.NET中有多少種定時器

来源:https://www.cnblogs.com/czwy/archive/2023/11/28/17862702.html
-Advertisement-
Play Games

一般在會議、教學或培訓活動中,我們都會選擇PPT文檔來進行內容展示。與PDF文檔相比,PPT文檔具有較強的可編輯性,可以隨時增刪元素,並且還可以設置豐富多樣的動畫效果來吸引觀眾註意。那麼如何通過C#將PDF文檔轉為PPT文檔呢?本文將教大家僅使用3行代碼就實現這一功能。 PDF轉PPT所需工具: S ...


.NET中至少有6種定時器,每一種定時器都有它的用途和特點。根據定時器的應用場景,可以分為UI相關的定時器和UI無關的定時器。本文將簡單介紹這6種定時器的基本用法和特點。

UI定時器

.NET中的UI定時器主要是WinForm、WPF以及WebForm中的定時器。分別為:

  • System.Windows.Forms.Timer
  • System.Windows.Threading.DispatcherTimer
  • System.Web.UI.Timer

通常情況下,WinForm、WPF中的定時器是在UI線程上執行回調函數,因此可以直接訪問UI元素。由於WinForm、WPF支持單線程單元模型(Single-Thread Apartment,STA),定時器間隔事件是在UI線程上觸發,因此,不用擔心線程安全問題。
System.Web.UI.Timer是通過Javascript定時器和服務端非同步回調實現,也是單線程的。

請註意,這裡說的是通常情況,後邊介紹System.Windows.Threading.DispatcherTimer時會提到在非UI線程創建DispatcherTimer時也無法直接訪問UI元素。

System.Windows.Forms.Timer

System.Windows.Forms.Timer針對WinForm應用進行了優化,是只能在WinForm上使用的定時器。這個定時器是針對單線程環境設計的,是在UI線程上處理定時任務。
它要求用戶代碼有可用的UI消息泵,定時任務須在UI線程上運行,或者跨線程通過Invoke或者BeginInvoke封送(marshal)到UI線程上運行。其優點是使用簡單,只需通過給Interval屬性賦值來設置時間間隔,並註冊Tick事件處理定時任務。其缺點是精度不高,精度為55毫秒,也就是Interval賦值小於55時,也是55毫秒觸發一次定時任務。

public partial class TimerFrom : Form
{
    private System.Windows.Forms.Timer digitalClock;
    private void TimerFrom_Load(object sender, EventArgs e)
    {
        digitalClock = new System.Windows.Forms.Timer();//創建定時器 
        digitalClock.Tick += new EventHandler(HandleTime);//註冊定時任務事件 
        digitalClock.Interval = 1000;//設置時間間隔
        digitalClock.Enabled = true;
        digitalClock.Start(); //開啟定時器
    }
    public void HandleTime(Object myObject, EventArgs myEventArgs)
    {
        labelClock.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
    }
    private void frmTimerDemo_FormClosed(object sender, FormClosedEventArgs e)
    {
        digitalClock.Stop();//停止定時器
        digitalClock.Dispose();
    }
}

System.Windows.Threading.DispatcherTimer

System.Windows.Threading.DispatcherTimer是WPF中的定時器,它是基於Dispatcher對象的(並不是基於UI線程的)。DispatcherTimer的定時任務是像其他操作一樣放在Dispatcher隊列上,其執行操作時間依賴於隊列中其他任務及其優先順序,因此,DispatcherTimer不保證在時間間隔發生時準確執行,只保證不會在時間間隔發生前執行。

Dispatcher為特定線程維護工作項(操作)的優先順序隊列,線上程上創建Dispatcher對象時,它成為唯一可以關聯該線程的Dispatcher對象,WPF中, DispatcherObject只能被與之關聯的Dispatcher對象訪問,也就是非UI線程中無法直接訪問UI元素(WPF中的UI元素都是派生自 DispatcherObject

此外,DispatcherTimer不像System.Windows.Forms.Timer那樣只在UI線程上創建才能觸發Tick事件,它在非UI線程下創建也可以觸發Tick事件,此時訪問UI元素也需要通過Invoke或者BeginInvoke封送(marshal)到UI線程上運行。其優點也是簡單易用,適合在UI線程上執行任務或觸發事件,缺點是精度不准確,可能存在延遲。

private void Dt_Tick(object sender, EventArgs e)
{
    Dispatcher.BeginInvoke((Action)delegate ()
    {
        text1.Text = DateTime.Now.ToString();
    });
    Console.WriteLine(DateTime.Now.ToString());
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    Task.Run(() =>{
        DispatcherTimer dt = new DispatcherTimer();
        dt.Tick += Dt_Tick;
        dt.Interval = TimeSpan.FromSeconds(1);
        dt.Start();
        Dispatcher.Run();
    });
}

上述代碼中,DispatcherTimer是非UI線程中創建,定時任務中訪問UI元素text1,需要通過Invoke或者BeginInvoke封送(marshal)到UI線程上運行,而Console.WriteLine則可以直接運行。

System.Web.UI.Timer

System.Web.UI.Timer是僅適用於.NET FrameworkASP.NET組件。通過Javascript定時器和服務端非同步回調實現。每次觸發定時器時,只能執行一個非同步回調方法,而其他的非同步回調方法需要等待前一個非同步回調方法執行完畢後才能執行。這樣可以保證在任意時刻只有一個非同步回調方法在執行,避免了多線程併發執行的問題。

UI無關定時器

從 .NET 6開始,UI無關定時器有三個:

  • System.Threading.Timer
  • System.Timers.Timer
  • System.Threading.PeriodicTimer(.NET 6+)

System.Threading.Timer

System.Threading.Timer是最基礎輕量的定時器,它將定期線上程池線程上執行單個回調方法。在創建定時器對象時必須指定回調方法,並且後續不能修改,同時也可以指定定時器回調開始執行的時間以及時間間隔。定時器創建後可以通過Change方法修改回調開始執行的時間以及時間間隔。該定時器的優點是輕量,精度相對較高,與Windows操作系統時鐘精度一致,大約15毫秒。但因為是基於線程池的,所以在任務執行時間較長或者線程池過載時,會出現延遲。其缺點是使用不太方便,定時器創建後無法修改回調方法。

var stateTimer = new 
var autoEvent = new AutoResetEvent(false);
Timer(CheckStatus, autoEvent, 1000,250);

private int invokeCount=0;

public void CheckStatus(Object stateInfo)
{
    AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
    Console.WriteLine("{0} Checking status {1,2}.",DateTime.Now.ToString("h:mm:ss.fff"),(++invokeCount).ToString());

    if(invokeCount == 10)
    {
        invokeCount = 0;
        autoEvent.Set();
    }
}

System.Timers.Timer

System.Timers.Timer在內部使用System.Threading.Timer,並公開了更多的屬性,如AutoReset, EnabledSynchronizingObject,這些屬性允許配置回調的執行方式。此外,Tick事件允許註冊多個處理程式。因此,一個定時器可以觸發多個處理程式。還可以在計時器啟動後更改處理程式。與System.Threading.Timer相似,其優點也是精度相對較高,與Windows操作系統時鐘精度一致,大約15毫秒。因為預設(或者SynchronizingObject=null時)是基於線程池的,所以在任務執行時間較長或者線程池過載時,會出現延遲。但使用要更簡便一些。

public partial class TimerFrom : Form
{
    private System.Timers.Timer timer;
    private void TimerFrom_Load(object sender, EventArgs e)
    {
        // 支持註冊多個處理程式
        timer.Elapsed += (sender, e) => { label1.Text = DateTime.Now.ToLongTimeString(); };
        timer.Elapsed += (sender, e) => { Console.WriteLine(DateTime.Now.ToLongTimeString()); };
        //自定義回調執行的方式(指定對象所在的線程),SynchronizingObject=null時線上程池上執行
        timer.SynchronizingObject = this;
        timer.AutoReset = true;
        timer.Start();
    }
}

本例中將SynchronizingObject屬性設置為Form對象,因此Elapsed的處理程式在UI線程上執行,可以直接修改 label1.Text,如果SynchronizingObject屬性為null,處理程式則是線上程池線程上執行,修改 label1.Text時需要通過Invoke或者BeginInvoke封送(marshal)到UI線程上運行。

System.Threading.PeriodicTimer

System.Threading.PeriodicTimer是 .NET 6中引入的定時器。它能方便地使用非同步方式,它沒有Tick事件,而是提供WaitForNextTickAsync方法處理定時任務。通常是使用While迴圈結合CancellationToken一起使用。和CancellationToken一起用的時候需要註意,如果CancellationToken被取消的時候會拋出一個OperationCanceledException需要考慮自己處理異常。相比之前的定時器來說,有下麵幾個特點:[1]

  1. 沒有callback 來綁定事件;
  2. 不會發生重入,只允許有一個消費者,不允許同一個PeriodicTimer在不同的地方同時WaitForNextTickAsync,不需要自己做排他鎖來實現不能重入;
  3. 非同步化。之前的 timer 的 callback 都是同步的,使用新 timer 可以使用非同步方法,避免了編寫 Sync over Async 代碼;
  4. Dispose 之後,實例就無法使用,並且 WaitForNextTickAsync 始終返回 false。
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
using (var timer = new PeriodicTimer(TimeSpan.FromSeconds(1)))
{
    try
    {
        while (await timer.WaitForNextTickAsync(cts.Token))
        {
            await Task.Delay(3000);
            Console.WriteLine($"ThreadId is {Thread.CurrentThread.ManagedThreadId} --- Time is {DateTime.Now:HH:mm:ss}");
        }
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("Operation cancelled");
    }
}

小結

我們在開發過程中遇到的坑往往不是技術本身的坑,而是我們濫用沒有掌握的技術導致的,在有多種技術方案可選的時候,通常只關註技術的優點,忽略了技術適用場景及其局限性。.NET中幾種定時器各自都有其適用場景和不足,但都不支持高精度計時。瞭解這些有助於我們在開發過程中選擇合適定時器,避免遇到問題後被動地替換解決方案。


  1. https://xie.infoq.cn/article/6aa23b6850abddf717a6c9fc9 ↩︎


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • AES(Advanced Encryption Standard)是一種對稱加密演算法,它是目前廣泛使用的加密演算法之一。AES演算法是由美國國家標準與技術研究院(NIST)於2001年發佈的,它取代了原先的DES(Data Encryption Standard)演算法,成為新的標準。AES是一種對稱加密... ...
  • 【先來個小測試】 大家覺得下麵的sql返回什麼? select * from table1 where null=1 答案:無返回。因為null=1是個false的表達式。這就像我們寫where 1=2一樣。 【↓↓正文開始↓↓】 需求開發完成,將開發分支merge到test分支,部署測試環境提測後 ...
  • 原文 : https://openaigptguide.com/chatgpt-aigc-difference/ AIGC 和 ChatGPT 都是人工智慧技術,但它們的功能和應用場景不同。 AIGC(AI-GeneratedContent,人工智慧自動生成內容)是人工智慧、電腦圖形學和深度學習等 ...
  • 你可以使用 Python 中的列表拼接操作來合併兩個或多個列表。Python 提供了幾種方式來實現列表的拼接,包括使用 + 運算符、extend() 方法和列表解析。 以下是這些方法的示例: 1.使用 + 運算符: list1 = [1, 2, 3] list2 = [4, 5, 6] concat ...
  • Python還提供了許多其他用於數據處理和轉換的內置函數和模塊。以下是一些常用的數據處理函數和模塊: sorted sorted(iterable, key=func, reverse=False) 用於對可迭代對象進行排序。你可以指定一個可選的 key 函數來自定義排序規則,以及一個可選的 rev ...
  • HTTP(Hypertext Transfer Protocol)是一種用於傳輸超文本的協議。它是一種無狀態的、應用層的協議,用於在電腦之間傳輸超文本文檔,通常在 Web 瀏覽器和 Web 伺服器之間進行數據通信。HTTP 是由互聯網工程任務組(IETF)定義的,它是基於客戶端-伺服器模型的協議,... ...
  • 通過實現一個簡易版本的跳錶,可以加深了對Python編程的理解。跳錶是一種跳躍式的數據結構,通過索引層提供快速查找的能力,提高了查找的效率。在實現跳錶的過程中,會更加熟悉了Python的語法和特性,並且可以更加靈活地運用它來解決實際問題。 ...
  • 1、定義 Cron表達式是一種用於定義定時任務的格式化字元串。它被廣泛用於Unix、Linux和類Unix系統中,用於在指定的時間執行預定的任務。Cron表達式由6個欄位組成,每個欄位通過空格分隔開。 在本文中,我們將學習如何理解和編寫Cron表達式。 Cron表達式的格式如下: * * * * * ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...