C# Retry重试操作解决方案(附源码)

一、前言

(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定时触发进度条循环滚动,后台线程执行相应操作,线程执行完以后再设置相应内容。

 源码下载地址重试解决方案文件


<script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C# 中实现支付回调的重试通知机制的一般流程如下: 1. 接收到支付回调请求。 2. 处理支付回调请求,如果处理失败,则记录请求信息到数据库中,并设置重试次数为 0。 3. 启动计时器,根据重试次数和时间间隔进行通知。 4. 计时器触发后,从数据库中读取未成功处理的请求信息,进行重试通知。 5. 如果重试成功,则从数据库中删除请求信息;如果重试失败,则将重试次数加 1,并更新数据库中的请求信息。 6. 重复步骤 4 和 5,直到达到最大重试次数。 下面是一个简单的 C# 代码示例,实现了支付回调的重试通知机制: ```csharp using System; using System.Collections.Generic; using System.Data.SqlClient; using System.ServiceProcess; using System.Threading; namespace PaymentNotificationService { public partial class PaymentNotificationService : ServiceBase { private Timer retryTimer; private const int MAX_RETRY_COUNT = 8; private const int TIME_INTERVAL_1 = 4 * 60 * 1000; // 4 minutes private const int TIME_INTERVAL_2 = 10 * 60 * 1000; // 10 minutes private const int TIME_INTERVAL_3 = 1 * 60 * 60 * 1000; // 1 hour private const int TIME_INTERVAL_4 = 2 * 60 * 60 * 1000; // 2 hours private const int TIME_INTERVAL_5 = 6 * 60 * 60 * 1000; // 6 hours private const int TIME_INTERVAL_6 = 15 * 60 * 60 * 1000; // 15 hours public PaymentNotificationService() { InitializeComponent(); } protected override void OnStart(string[] args) { // 启动计时器 retryTimer = new Timer(new TimerCallback(RetryNotification), null, 0, Timeout.Infinite); } protected override void OnStop() { // 停止计时器 retryTimer.Change(Timeout.Infinite, Timeout.Infinite); } private void RetryNotification(object state) { // 从数据库中读取未成功处理的请求信息 List<PaymentNotification> notifications = GetUnprocessedNotifications(); foreach (PaymentNotification notification in notifications) { if (notification.RetryCount < MAX_RETRY_COUNT) { // 根据重试次数设置时间间隔 int timeInterval = 0; switch (notification.RetryCount) { case 0: timeInterval = 0; break; case 1: timeInterval = TIME_INTERVAL_1; break; case 2: timeInterval = TIME_INTERVAL_2; break; case 3: timeInterval = TIME_INTERVAL_3; break; case 4: timeInterval = TIME_INTERVAL_4; break; case 5: timeInterval = TIME_INTERVAL_5; break; case 6: timeInterval = TIME_INTERVAL_6; break; default: break; } // 进行重试通知 bool success = Notify(notification); if (success) { // 重试成功,从数据库中删除请求信息 DeleteNotification(notification); } else { // 重试失败,更新数据库中的请求信息 UpdateNotification(notification); } // 进入下一次计时 retryTimer.Change(timeInterval, Timeout.Infinite); } else { // 达到最大重试次数,从数据库中删除请求信息 DeleteNotification(notification); } } } private List<PaymentNotification> GetUnprocessedNotifications() { // 从数据库中读取未成功处理的请求信息 List<PaymentNotification> notifications = new List<PaymentNotification>(); using (SqlConnection connection = new SqlConnection("your_connection_string")) { connection.Open(); SqlCommand command = new SqlCommand("SELECT * FROM PaymentNotifications WHERE RetryCount < @maxRetryCount", connection); command.Parameters.AddWithValue("@maxRetryCount", MAX_RETRY_COUNT); SqlDataReader reader = command.ExecuteReader(); while (reader.Read()) { PaymentNotification notification = new PaymentNotification(); notification.Id = reader.GetInt32(0); notification.OrderId = reader.GetString(1); notification.Amount = reader.GetDecimal(2); notification.RetryCount = reader.GetInt32(3); notification.CreateTime = reader.GetDateTime(4); notifications.Add(notification); } reader.Close(); } return notifications; } private bool Notify(PaymentNotification notification) { // 实现通知的具体逻辑 // ... return true; } private void UpdateNotification(PaymentNotification notification) { // 更新数据库中的请求信息 using (SqlConnection connection = new SqlConnection("your_connection_string")) { connection.Open(); SqlCommand command = new SqlCommand("UPDATE PaymentNotifications SET RetryCount = @retryCount WHERE Id = @id", connection); command.Parameters.AddWithValue("@retryCount", notification.RetryCount + 1); command.Parameters.AddWithValue("@id", notification.Id); command.ExecuteNonQuery(); } } private void DeleteNotification(PaymentNotification notification) { // 从数据库中删除请求信息 using (SqlConnection connection = new SqlConnection("your_connection_string")) { connection.Open(); SqlCommand command = new SqlCommand("DELETE FROM PaymentNotifications WHERE Id = @id", connection); command.Parameters.AddWithValue("@id", notification.Id); command.ExecuteNonQuery(); } } } public class PaymentNotification { public int Id { get; set; } public string OrderId { get; set; } public decimal Amount { get; set; } public int RetryCount { get; set; } public DateTime CreateTime { get; set; } } } ``` 在上面的代码中,我们使用了一个 `Timer` 计时器来实现支付回调的重试通知机制。在 `OnStart` 方法中启动计时器,在 `OnStop` 方法中停止计时器。计时器在 `RetryNotification` 方法中触发,从数据库中读取未成功处理的请求信息,进行重试通知。在 `Notify` 方法中实现了具体的通知逻辑,您可以根据自己的需求进行修改。在 `UpdateNotification` 和 `DeleteNotification` 方法中分别实现了更新和删除请求信息的逻辑。同时,代码中的时间间隔参数也可以根据您的需求进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值