| 特性 | WinForms Timer | Timers.Timer | Threading.Timer |
|---|---|---|---|
| 命名空間 | System.Windows.Forms | System.Timers | System.Threading |
| 執行緒模型 | UI 執行緒 (單執行緒) | 執行緒池 (多執行緒) | 執行緒池 (多執行緒) |
| 觸發事件執行緒 | UI 執行緒 (安全操作控制項) | 執行緒池執行緒 (需 Marshal 回 UI) | 執行緒池執行緒 (需 Marshal 回 UI) |
| 精確度 | 低 (~55ms 解析度) | 中高 (系統計時器) | 高 (系統計時器) |
| 適用場景 | UI 動畫、定時刷新介面 | 伺服器端定時任務、需同步 UI 時 | 高頻背景任務、無 UI 需求 |
| IDisposable | 是 | 是 | 是 |
| Stop/Start 方法 | Stop()/Start() | Stop()/Start() | Change(Timeout.Infinite, ...)/Change(...) |
| 自動重置 | 是 (Interval 週期) | AutoReset 屬性控制 | 週期參數控制 |
public partial class MainForm : Form
{
private System.Windows.Forms.Timer _uiTimer;
public MainForm()
{
InitializeComponent();
_uiTimer = new System.Windows.Forms.Timer { Interval = 2000 };
_uiTimer.Tick += UiTimer_Tick;
}
private void btnStart_Click(object sender, EventArgs e)
{
_uiTimer.Start();
}
private void UiTimer_Tick(object sender, EventArgs e)
{
// 直接安全操作 UI 控制項
lblTime.Text = DateTime.Now.ToString("HH:mm:ss.fff");
RefreshDataGrid(); // 直接繫結資料
}
private void RefreshDataGrid()
{
// 同步資料庫查詢 (若耗時長會凍結 UI,建議改非同步)
using (var conn = new SqlConnection(connStr))
using (var da = new SqlDataAdapter("SELECT * FROM Logs ORDER BY Id DESC", conn))
{
var dt = new DataTable();
da.Fill(dt);
dataGridView1.DataSource = dt;
}
}
}
⚠️ Tick 事件在 UI 執行緒執行,**嚴禁執行耗時操作** (資料庫查詢、網路請求、大量運算),否則介面會卡死。耗時工作請用 Task.Run 配合 await。
public partial class MainForm : Form
{
private System.Timers.Timer _serverTimer;
public MainForm()
{
InitializeComponent();
_serverTimer = new System.Timers.Timer(2000);
_serverTimer.Elapsed += ServerTimer_Elapsed;
_serverTimer.SynchronizingObject = this; // 關鍵:自動 Marshal 回 UI 執行緒
}
private void ServerTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
// 此處已在 UI 執行緒 (因 SynchronizingObject)
// 但建議仍用非同步避免阻塞計時器執行緒
_ = RefreshDataAsync();
}
private async Task RefreshDataAsync()
{
try
{
using (var conn = new SqlConnection(connStr))
await conn.OpenAsync();
using (var cmd = new SqlCommand("SELECT * FROM Logs ORDER BY Id DESC", conn))
using (var reader = await cmd.ExecuteReaderAsync())
{
var dt = new DataTable();
dt.Load(reader);
// UI 繫結需在 UI 執行緒 (SynchronizingObject 已保證)
dataGridView1.DataSource = dt;
}
}
catch (Exception ex)
{
// 錯誤處理
}
}
}
SynchronizingObject = this 讓 Elapsed 事件自動透過 Control.Invoke 回 UI 執行緒,免寫手動 Invoke 程式碼。
public class BackgroundService : IDisposable
{
private System.Threading.Timer _timer;
private readonly Action _work;
public BackgroundService(Action work, TimeSpan dueTime, TimeSpan period)
{
_work = work;
// dueTime: 首次延遲, period: 週期 (Timeout.InfiniteTimeSpan 為單次)
_timer = new System.Threading.Timer(_ => ExecuteWork(), null, dueTime, period);
}
private void ExecuteWork()
{
try
{
_work(); // 在執行緒池執行緒執行
}
catch (Exception ex)
{
// 記錄日誌,避免未觀測例外終結進程
}
}
public void Dispose() => _timer?.Dispose();
// 動態變更週期
public void ChangePeriod(TimeSpan newPeriod) => _timer.Change(TimeSpan.Zero, newPeriod);
}
// 使用
var service = new BackgroundService(() =>
{
Console.WriteLine($"背景工作執行:{DateTime.Now}");
// 發送心跳、清理快取、同步資料...
}, TimeSpan.Zero, TimeSpan.FromMinutes(5));
// 取代所有 Timer:使用 Task.Delay 迴圈,完整支援 async/await、取消權杖、例外處理
public class AsyncPoller
{
private readonly Func _action;
private readonly TimeSpan _interval;
private readonly CancellationTokenSource _cts = new();
private Task _pollingTask;
public AsyncPoller(Func action, TimeSpan interval)
{
_action = action;
_interval = interval;
}
public void Start() => _pollingTask = PollLoopAsync(_cts.Token);
public async Task StopAsync()
{
_cts.Cancel();
if (_pollingTask != null) await _pollingTask;
}
private async Task PollLoopAsync(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
try
{
await _action();
}
catch (Exception ex)
{
// 記錄錯誤,決定是否繼續
}
try
{
await Task.Delay(_interval, token);
}
catch (OperationCanceledException) { break; }
}
}
}
// 使用
var poller = new AsyncPoller(async () =>
{
await RefreshDataFromApiAsync();
UpdateUI(); // 需在 UI 執行緒
}, TimeSpan.FromSeconds(30));
poller.Start();
// 程式結束時
await poller.StopAsync();
System.Windows.Forms.Timer (最簡單、最安全)System.Threading.Timer 或 AsyncPoller (輕量、精確)System.Timers.Timer + SynchronizingObject 或 AsyncPoller + Control.InvokeAsyncPoller (Task.Delay 迴圈)**timer.Stop(),結尾 timer.Start()。AutoReset=true (預設) 且處理時間超過 Interval 會在執行緒池併發觸發。解決:AutoReset=false 手動重啟,或用 lock 保護共享資源。IDisposable 確保釋放。var i = index;。