網路呼叫、資料庫操作、外部 API 等易發生暫時性失敗。單純 try-catch 重試會阻塞 UI 執行緒,且缺乏統一的重試策略、進度回報與取消機制。本文封裝 RetryProvider,基於 BackgroundWorker 實作非同步重試,支援自訂等待間隔、重試次數、進度事件與取消。
RetryProvider 暴露兩個進入點:
StartAsync(Action target, int waitTimeout, int retryCount):無回傳值委托。StartAsyncFunc(Func<bool> target, int waitTimeout, int retryCount):回傳 bool 表示成功與否。內部統一由 StartAsyncRetry 建立/重用 BackgroundWorker,設定 WorkerSupportsCancellation 與 WorkerReportsProgress。
private void Start(object target)
{
int retryCount = this.RetryCount;
lock (_asyncLock)
{
_backgroupThread.ReportProgress(5);
while (!_backgroupThread.CancellationPending)
{
PerRetryBegin?.Invoke(RetryCount - retryCount);
try
{
if (target is Action action) { action.Invoke(); InvokeCompletedEvent(true); return; }
else if (target is Func<bool> func)
{
if (func.Invoke()) { InvokeCompletedEvent(true); return; }
throw new InvalidOperationException("Execute Failed.");
}
}
catch (Exception ex)
{
PerRetryFailedCompleted?.Invoke(ex);
_backgroupThread.ReportProgress((RetryCount - retryCount + 1) * 100 / RetryCount);
}
finally { PerRetryEnd?.Invoke(RetryCount - retryCount); }
if (RetryCount > 0)
{
retryCount--;
if (retryCount == 0) { InvokeCompletedEvent(); return; }
}
Thread.Sleep(WaitTimeout);
}
if (_backgroupThread.CancellationPending) Cancelled?.Invoke();
}
}
PerRetryBegin(int retryIndex):單次重試開始 (索引從 0 起)。PerRetryEnd(int retryIndex):單次重試結束。PerRetryFailedCompleted(Exception ex):單次失敗異常資訊。ProgressChanged(int percent):整體進度百分比。Completed(bool success):最終完成狀態。Cancelled():使用者取消。private RetryProvider _retryProvider;
private void btnStart_Click(object sender, EventArgs e)
{
int waitTimeout = (int)nupWaitTimeout.Value * 1000;
int retryCount = (int)nupRetryCount.Value;
if (_retryProvider == null)
{
_retryProvider = new RetryProvider();
_retryProvider.PerRetryFailedCompleted += (ex) => Log($"重試失敗: {ex.Message}");
_retryProvider.Cancelled += () => Log("已取消");
_retryProvider.PerRetryBegin += (i) => Log($"第 {i+1} 次重試開始");
_retryProvider.PerRetryEnd += (i) => Log($"第 {i+1} 次重試結束");
_retryProvider.ProgressChanged += (p) => progressBar.Value = p;
_retryProvider.Completed += (ok) => { btnStart.Enabled = true; Log(ok ? "成功" : "最終失敗"); };
}
if (_retryProvider.IsBusy) return;
btnStart.Enabled = false;
_retryProvider.StartAsyncFunc(ThrowExceptionMethod, waitTimeout, retryCount);
}
private bool ThrowExceptionMethod()
{
// 模擬不穩定操作
if (DateTime.Now.Second % 3 == 0) return true;
throw new Exception("隨機失敗");
}
private void btnCancel_Click(object sender, EventArgs e) => _retryProvider?.Cancel();
Timer 輪詢進度條。原始碼下載:重試解決方案原始碼