Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 740|回复: 0

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

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-6-11 15:04:50 | 显示全部楼层 |阅读模式

    一、前言

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

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

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-12-23 07:42 , Processed in 0.058680 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表