首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 多執行緒自動管理:Timer 定時器完整教學與範例

     編輯:關於C#

前言

在多執行緒應用中,常見需求是**定期執行某項工作**,例如:定時掃描資料夾、定時發送心跳封包、定時清理快取、輪詢資料庫狀態。若手動建立執行緒配合 Thread.Sleep 迴圈,不僅程式碼冗長,且難以精確控制間隔、暫停、繼續或動態調整頻率。.NET 提供的 System.Threading.Timer 類別,專為這類**背景週期性工作**設計:它使用**執行緒池** 執行回呼、支援動態變更間隔、無需手動管理執行緒生命週期。本文將完整解析 Timer 的建構函式、Change 方法、Dispose 模式,並提供 MSDN 經典範例的現代化重寫版本。

Timer 核心特性

  • 執行緒池驅動:回呼在 ThreadPool 執行緒上執行,**不阻塞主執行緒**,也不佔用專屬執行緒。
  • 非同步回呼:回呼方法簽名為 void Callback(object state),需自行處理執行緒安全。
  • 精確度:約 10-15 毫秒解析度 (受系統時鐘中斷影響),不適合高精度即時控制。
  • 可重入風險:若回呼執行時間超過間隔,**會並行執行多個回呼**,需自行加鎖或使用 Change(Timeout.Infinite, ...) 暫停。

建構函式完整簽名

public Timer(TimerCallback callback, object state, int dueTime, int period);
參數型別說明
callbackTimerCallback委派,指向要週期性執行的方法 void Method(object state)
stateobject傳遞給回呼的自訂狀態物件,可為 null
dueTimeint (毫秒)**首次延遲**多久觸發回呼。0 = 立即執行;Timeout.Infinite = 永不啟動
periodint (毫秒)**週期間隔**。Timeout.Infinite = 僅執行一次 (一次性計時器)

核心方法

方法用途
Change(int dueTime, int period)動態修改啟動延遲與週期間隔。傳入 Timeout.Infinite 可停用定期觸發。
Dispose()釋放 Timer 資源,**會等待正在執行的回呼完成** (同步等待)。配合 Dispose(WaitHandle) 可非同步等待。

完整範例:動態調整間隔與自動停止

此範例改寫自 MSDN 經典範例,展示:

  1. 建立每 1 秒觸發一次的 Timer
  2. 第 5 次觸發時,動態改為 10 秒後啟動、每 2 秒觸發一次
  3. 第 10 次觸發時,釋放 Timer 並結束程式
using System;
using System.Threading;

namespace TimerDemo
{
    /// 
    /// 封裝 Timer 狀態的類別,避免使用 static 欄位
    /// 
    class TimerState
    {
        public int Counter { get; set; } = 0;
        public Timer TimerRef { get; set; }
    }

    class Program
    {
        static void Main()
        {
            Console.WriteLine("=== System.Threading.Timer 示範 ===");

            var state = new TimerState();

            // 建立 TimerCallback 委派指向靜態回呼方法
            TimerCallback callback = CheckStatus;

            // 建立 Timer:1秒後首次觸發,之後每1秒觸發一次
            Timer timer = new Timer(callback, state, 1000, 1000);
            state.TimerRef = timer; // 讓回呼能存取 Timer 實例以呼叫 Change/Dispose

            Console.WriteLine("主執行緒等待 Timer 結束 (按任意鍵可強制結束)...");

            // 主執行緒等待:輪詢 TimerRef 是否為 null (Dispose 後設為 null)
            // 實務上建議使用 ManualResetEventSlim 或 TaskCompletionSource 等待
            while (state.TimerRef != null)
            {
                Thread.Sleep(100);
                if (Console.KeyAvailable) break; // 允許手動中斷
            }

            Console.WriteLine("Timer 範例結束。");
            Console.ReadKey();
        }

        /// 
        /// Timer 回呼方法 (在 ThreadPool 執行緒執行)
        /// 
        static void CheckStatus(object stateObj)
        {
            var state = (TimerState)stateObj;
            state.Counter++;

            Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 第 {state.Counter} 次檢查");

            // 第 5 次:動態調整間隔
            if (state.Counter == 5)
            {
                Console.WriteLine("  → 變更間隔:10秒後重新開始,之後每2秒觸發一次");
                // dueTime=10000 (10秒後首次),period=2000 (每2秒)
                state.TimerRef.Change(10000, 2000);
            }

            // 第 10 次:停止並釋放 Timer
            if (state.Counter == 10)
            {
                Console.WriteLine("  → 計數達 10,釋放 Timer 並結束");
                state.TimerRef.Dispose(); // 同步等待回呼完成
                state.TimerRef = null;    // 通知主執行緒迴圈結束
            }
        }
    }
}

程式執行流程說明

[14:30:00.000] 第 1 次檢查
[14:30:01.000] 第 2 次檢查
[14:30:02.000] 第 3 次檢查
[14:30:03.000] 第 4 次檢查
[14:30:04.000] 第 5 次檢查
  → 變更間隔:10秒後重新開始,之後每2秒觸發一次
[14:30:14.000] 第 6 次檢查  (10秒後)
[14:30:16.000] 第 7 次檢查
[14:30:18.000] 第 8 次檢查
[14:30:20.000] 第 9 次檢查
[14:30:22.000] 第 10 次檢查
  → 計數達 10,釋放 Timer 並結束
Timer 範例結束。

進階用法與注意事項

1. 一次性計時器 (Single-shot Timer)

// 5 秒後執行一次,不重複
Timer oneShot = new Timer(callback, state, 5000, Timeout.Infinite);

2. 立即啟動並週期執行

// dueTime=0 立即觸發第一次
Timer immediate = new Timer(callback, state, 0, 1000);

3. 避免回呼重入 (Reentrancy) 問題

若回呼執行時間可能超過間隔,必須加鎖:

private readonly object _lock = new object();

void Callback(object state)
{
    if (!Monitor.TryEnter(_lock)) return; // 上次未完成,直接跳過
    try
    {
        // 實際工作...
    }
    finally { Monitor.Exit(_lock); }
}

或使用 Change(Timeout.Infinite, Timeout.Infinite) 暫停,工作完成後再 Change(0, period) 恢復。

4. 正確釋放 Timer (IDisposable 模式)

  • Dispose():同步等待目前正在執行的回呼完成,**會阻塞呼叫執行緒**。
  • Dispose(WaitHandle notifyObject):非同步釋放,回呼完成後會 Set() 指定的 WaitHandle (如 ManualResetEvent),適合 UI 執行緒避免凍結。
  • **務必呼叫 Dispose**,否則 Timer 會持續佔用執行緒池資源且無法被 GC 回收。

5. Timer vs 其他定時機制比較

類別命名空間執行緒模型適用場景UI 安全
System.Threading.TimerSystem.ThreadingThreadPool 背景執行緒伺服器端背景作業、高頻率、無 UI 互動否 (回呼非 UI 執行緒)
System.Timers.TimerSystem.TimersThreadPool (元件模型)元件化設計、支援 SynchronizingObject 導向 UI 執行緒是 (設定 SynchronizingObject)
System.Windows.Forms.TimerSystem.Windows.FormsUI 執行緒 (訊息迴圈)WinForms UI 更新、低頻率 (>50ms)是 (原生 UI 執行緒)
System.Windows.Threading.DispatcherTimerSystem.Windows.ThreadingUI 執行緒 (Dispatcher)WPF UI 更新是

總結

System.Threading.Timer 是 .NET 核心庫中最輕量、效能最好的**背景週期性工作**排程器。它完全由執行緒池驅動,無需手動管理執行緒,支援動態調整間隔 (Change) 與正確資源釋放 (Dispose)。掌握其「非同步回呼、可重入風險、IDisposable 模式」三大核心特性,即可在伺服器端、Windows Service、主控台應用中穩健實作定時掃描、輪詢、心跳、清理等常見需求。若涉及 UI 更新,請改用 System.Timers.Timer (配合 SynchronizingObject) 或 UI 框架專用 Timer。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved