一、前言

(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 = , int retryCount = )
{
StartAsyncRetry(target, waitTimeout, retryCount);
} public void StartAsyncFunc(Func<bool> target, int waitTimeout = , int retryCount = )
{
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(); 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 + ) * / this.RetryCount);
}
finally
{
if (this.PerRetryEnd != null)
{
this.PerRetryEnd(this.RetryCount - retryCount);
}
} if (this.RetryCount > )
{
retryCount--; if (retryCount == )
{
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 = ;
int.TryParse(this.nupWaitTimeout.Value.ToString(), out waitTimeout);
int retryCount = ;
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 > )
{
AddMsg(null);
} this._retryProvider.StartAsyncFunc(ThrowExceptionMethod, waitTimeout * , 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定时触发进度条循环滚动,后台线程执行相应操作,线程执行完以后再设置相应内容。

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

C# Retry重试操作解决方案(附源码)的更多相关文章

  1. 使用jsonp跨域调用百度js实现搜索框智能提示,并实现鼠标和键盘对弹出框里候选词的操作【附源码】

    项目中常常用到搜索,特别是导航类的网站.自己做关键字搜索不太现实,直接调用百度的是最好的选择.使用jquery.ajax的jsonp方法可以异域调用到百度的js并拿到返回值,当然$.getScript ...

  2. 2.NetDh框架之简单高效的日志操作类(附源码和示例代码)

    前言 NetDh框架适用于C/S.B/S的服务端框架,可用于项目开发和学习.目前包含以下四个模块 1.数据库操作层封装Dapper,支持多种数据库类型.多库实例,简单强大: 此部分具体说明可参考博客: ...

  3. POI导出大量数据的简单解决方案(附源码)-Java-POI导出大量数据,导出Excel文件,压缩ZIP(转载自iteye.com)

    说明:我的电脑 2.0CPU 2G内存 能够十秒钟导出 20W 条数据 ,12.8M的excel内容压缩后2.68M 我们知道在POI导出Excel时,数据量大了,很容易导致内存溢出.由于Excel ...

  4. jaxp实现对xml文档的增,删,改,查操作(附源码)浅析

    jaxp,属于javase中的一部分.是对xml进行解析的一个工具类: 既然说到这里,还是讲全一点,讲讲上面说到的xml的解析技术. xml的一个标记型文档. 在html的层级结构中,它会在内存中分配 ...

  5. opencvbase 实现opencv打开摄像头和初步处理等效果操作(附源码)

    // TwoCameraOnTimer2Dlg.cpp : 实现文件 /* CvMat, Mat, IplImage之间的互相转换 IpIImage -> CvMat CvMat mathead ...

  6. (原创)通用查询实现方案(可用于DDD)[附源码] -- 简介

    [声明] 写作不易,转载请注明出处(http://www.cnblogs.com/wiseant/p/3985353.html).   [系列文章] 通用查询实现方案(可用于DDD)[附源码] -- ...

  7. Entity Framework在Asp.net MVC中的实现One Context Per Request(附源码)

    上篇中"Entity Framework中的Identity map和Unit of Work模式", 由于EF中的Identity map和Unit of Work模式,EF体现 ...

  8. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(6)-Unity 2.x依赖注入by运行时注入[附源码]

    原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(6)-Unity 2.x依赖注入by运行时注入[附源码] Unity 2.x依赖注入(控制反转)IOC,对 ...

  9. 极限挑战—C#100万条数据导入SQL SERVER数据库仅用4秒 (附源码)

    原文:极限挑战-C#100万条数据导入SQL SERVER数据库仅用4秒 (附源码) 实际工作中有时候需要把大量数据导入数据库,然后用于各种程序计算,本实验将使用5中方法完成这个过程,并详细记录各种方 ...

随机推荐

  1. webstorm11怎么设置成sublime3的界面

    引入架包导入即可 下载路径:https://github.com/OtaK/jetbrains-monokai-sublime

  2. Mongodb 服务(windows环境下)因被强制关闭,导致服务不能启动的处理办法

    环境:windows 8操作系统下 前置条件:因操作系统原因,没有先停止mongodb服务,直接重启操作系统. 导致结果:mongodb服务无法启动,弹出框报错. Windows 无法启动MongoD ...

  3. 使用jekyll在GitHub Pages上搭建个人博客【转】

    网上有不少资源,但大多是“授人以鱼”,文中一步一步的告诉你怎么做,却没有解释为什么,以及他是如何知道的.他们默认着你知道种种专业名词的含义,默认着你掌握着特定技能.你折腾半天,查资料,看教程,一步步下 ...

  4. inotify resources exhausted

    inotify resources exhausted tail -f /var/log/kubelet.log tail: inotify resources exhausted tail: ino ...

  5. 20151208Study

    20151208-----------------------------------------------------* Her main interest now is raising her ...

  6. 在rails 4 中 使用 CSV 组件来 把csv文件导入到数据库

    class HardWorker include Sidekiq::Worker require 'CSV' def perform(file_path) csv_text = File.read(f ...

  7. Android中如何收听特定应用安装成功的广播

    一.manifest的配置 <receiver android:name=".AppReceiver"> <intent-filter> <actio ...

  8. HDU Math Problems

    1576 const int mod = 9973; n = a - a / mod * mod; a / b = ans; ans * b = a = a / mod * mod + n; n = ...

  9. 一句话的代码,从集合中找出第一个重复字符的方法javascript版。

    有的时候需求是这样的: 找出集合中第一个重复的字符所在的位置,刚才看了园内某自许为算法的代码,感觉非常之啰嗦故写了以下代码! 本人对神马算法之类的完全不懂,但那些伪算法家们也别出来装蒜.一句话:不要欺 ...

  10. Qt ffmpeg环境搭建

    ffmpeg下载地址:https://ffmpeg.zeranoe.com/builds/ 版本选择第一个,然后多少位看自己的pc(我的是64),右边对应三个都要下载,Static,Shared,De ...