程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> C# Retry重試操作的解決方案

C# Retry重試操作的解決方案

編輯:關於C#

一、前言

(1)對於Thread的Abort方法,如果線程當前正在執行的是一段非托管代碼,那麼CLR就不會拋出ThreadAbortException,只有當代碼繼續回到CLR中時,才會引發ThreadAbortException。當然,即便是在CLR環境中ThreadAbortException也不會立即引發。

(2)對於BackgroundWorker的CancelAsync方法,需要設置WorkerSupportsCancellation屬性為True,在執行方法內部檢測CancellationPending標識,用戶負責退出。

(3)對於CancellationTokenSource,場景主要為對任務設置一個預期執行時間,對超時的任務自動取消。達到時間間隔後自動觸發Cancel方法,IsCancellationRequested被設置為True,用戶同樣需要在方法內部檢測IsCancellationRequested屬性。

本文在基於上述基礎上,對於方法的Retry(重新執行)操作,執行時間可能比較久,容易導致主線程阻塞,因此主要以BackgroundWorker來執行相關操作。RetryProvider類圖以及相關操作示例圖如下:

二、RetryProvider的具體實現

RetryProvider主要用於對方法的重新執行操作,在後台線程BackgroundWorker對執行方法進行相應的控制和處理,主要提供以下2種方法:

(1)public void StartAsync(Action target, int waitTimeout = 0, int retryCount = 1)

(2)public void StartAsyncFunc(Func<bool> target, int waitTimeout = 0, int retryCount = 1)

第一種方法主要針對於無返回參數的方法,第二種方法主要針對於返回bool參數的方法,當然你也可以擴展該類,支持相關的其他參數的方法。方法具體如下所示:

public void StartAsync(Action target, int waitTimeout = 0, int retryCount = 1)
        {
            StartAsyncRetry(target, waitTimeout, retryCount);
        }
    
        public void StartAsyncFunc(Func<bool> target, int waitTimeout = 0, int retryCount = 1)
        {
            StartAsyncRetry(target, waitTimeout, retryCount);
        }
    
        private void StartAsyncRetry(object target, int waitTimeout, int retryCount)
        {
            if (target == null)
            {
                throw new ArgumentNullException("target");
            }
    
            if (this._backgroupThread == null)
            {
                this._backgroupThread = new BackgroundWorker();
                this._backgroupThread.WorkerSupportsCancellation = true;
                this._backgroupThread.WorkerReportsProgress = true;
                this._backgroupThread.DoWork += OnBackgroupThreadDoWork;
                this._backgroupThread.ProgressChanged += OnBackgroupThreadProgressChanged;
            }
    
            if (this._backgroupThread.IsBusy)
            {
                return;
            }
    
            this.WaitTimeout = waitTimeout;
            this.RetryCount = retryCount;
    
            this._backgroupThread.RunWorkerAsync(target);
        }

在後台線程中執行的方法,主要根據RetryCount和BackgroundWorker的CancellationPending屬性進行相應的控制和處理,同時根據方法重試次數來報告相關進度,代碼如下:

private void Start(object target)
        {
            if (target == null)
            {
                return;
            }
    
            int retryCount = this.RetryCount;
    
            lock (_asyncLock)
            {
                this._backgroupThread.ReportProgress(5);
    
                while (!this._backgroupThread.CancellationPending)
                {
                    if (this.PerRetryBegin != null)
                    {
                        this.PerRetryBegin(this.RetryCount - retryCount);
                    }
    
                    try
                    {
                        if (target.GetType() == typeof(Action))
                        {
                            (target as Action).Invoke();
                            InvokeCompletedEvent(true);
                            return;
                        }
                        else
                        {
                            if ((target as Func<bool>).Invoke())
                            {
                                InvokeCompletedEvent(true);
                                return;
                            }
                            else
                            {
                                throw new InvalidOperationException("Execute Failed.");
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        if (this.PerRetryFailedCompleted != null)
                        {
                            this.PerRetryFailedCompleted(ex);
                        }
    
                        this._backgroupThread.ReportProgress((this.RetryCount - retryCount + 1) * 100 / this.RetryCount);
                    }
                    finally
                    {
                        if (this.PerRetryEnd != null)
                        {
                            this.PerRetryEnd(this.RetryCount - retryCount);
                        }
                    }
    
                    if (this.RetryCount > 0)
                    {
                        retryCount--;
    
                        if (retryCount == 0)
                        {
                            InvokeCompletedEvent();
                            return;
                        }
                    }
    
                    Thread.Sleep(this.WaitTimeout);
                }
    
                if (this._backgroupThread.CancellationPending)
                {
                    if (this.Cancelled != null)
                    {
                        this.Cancelled();
                    }
                }
            }
        }

目前只提供Action和Func<bool>參數的重試方法,因此只需要檢測下參數類型(如target.GetType() == typeof(Action))就可以進行相應的操作。如果傳入的參數是Func<bool>類型的,目前的處理方式為:檢測方法是否執行成功,如果返回true,即if ((target as Func<bool>).Invoke()),則退出重試操作後台線程,不在執行相應重試操作,否則拋出異常繼續執行操作。

Thread.Sleep(this.WaitTimeout)主要為阻塞當前線程,等待設置的Timeout時間,然後繼續執行相關方法。

三、相關應用示例

(1)啟動重試方法,設置相應參數以及相關事件。

private void btnStart_Click(object sender, EventArgs e)
        {
            int waitTimeout = 0;
            int.TryParse(this.nupWaitTimeout.Value.ToString(), out waitTimeout);
            int retryCount = 0;
            int.TryParse(this.nupRetryCount.Value.ToString(), out retryCount);
    
            Start(waitTimeout, retryCount);
        }
    
        private void Start(int waitTimeout, int retryCount)
        {
            if (this._retryProvider == null)
            {
                this._retryProvider = new RetryProvider();
                this._retryProvider.PerRetryFailedCompleted += _retryProvider_PerRetryFailedCompleted;
                this._retryProvider.Cancelled += _retryProvider_Cancelled;
                this._retryProvider.PerRetryBegin += _retryProvider_PerRetryBegin;
                this._retryProvider.PerRetryEnd += _retryProvider_PerRetryEnd;
                this._retryProvider.ProgressChanged += _retryProvider_ProgressChanged;
                this._retryProvider.Completed += _retryProvider_Completed;
            }
    
            if (this._retryProvider.IsBusy)
            {
                return;
            }
    
            this.btnStart.Enabled = false;
    
            if (this.listBoxRecord.Items.Count > 0)
            {
                AddMsg(null);
            }
    
            this._retryProvider.StartAsyncFunc(ThrowExceptionMethod, waitTimeout * 1000, retryCount);
        }

相應的事件如下:

public delegate void CompletedEventHandler(bool success);            //RetryProvider完成委托

public event CompletedEventHandler Completed;                           //RetryProvider完成事件

public delegate void CancelledEventHandler();                               //RetryProvider取消委托

public event CancelledEventHandler Cancelled;                              //RetryProvider取消事件

public delegate void PerRetryBeginEventHandler(int retryIndex);     //RetryProvider每一次重試開始操作委托,參數retryIndex重試次數,從0開始

public event PerRetryBeginEventHandler PerRetryBegin;                 //RetryProvider每一次重試開始操作事件

public delegate void PerRetryEndEventHandler(int retryIndex);       //RetryProvider每一次重試結束操作委托,參數retryIndex重試次數,從0開始

public event PerRetryEndEventHandler PerRetryEnd;                      //RetryProvider每一次重試結束操作事件

public delegate void PerRetryFailedEventHandler(Exception ex);      //RetryProvider每一次重試失敗委托,參數Exception為失敗的異常信息

public event PerRetryFailedEventHandler PerRetryFailedCompleted; //RetryProvider每一次重試失敗事件

public delegate void ProgressChangedEventHandler(int percent);     //RetryProvider進度更改委托

public event ProgressChangedEventHandler ProgressChanged;        //RetryProvider進度更改事件

(2)取消重試操作:

private void btnCancel_Click(object sender, EventArgs e)
        {
            if (this._retryProvider != null)
            {
                this._retryProvider.Cancel();
            }
        }

該方法將導致後台線程執行CancelAsync操作,在重試操作中,如果檢測到後台線程的CancellationPending為true,則會觸發Cancelled事件,退出後台線程。

六、總結

RetryProvider的主要是針對重試操作的封裝。考慮重試操作的操作時間可能較長,因此采用BackgroundWorker來進行相應的處理和控制。這種主要用於執行操作可控制的情況,對於不可控的,比如調用第三方程序進行相應操作(如調用MATLAB進行命令操作),等待時間和執行時間不確定,最佳方法為用Timer定時觸發進度條循環滾動,後台線程執行相應操作,線程執行完以後再設置相應內容。

源碼下載地址:重試解決方案文件

http://files.cnblogs.com/jasenkin/RetrySolution.rar

作者:JasenKin

出處:http://www.cnblogs.com/jasenkin

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved