林德熙 小伙伴希望保存一个文件,并且希望如果出错了也要不断地重试。然而我认为如果一直错误则应该对外抛出异常让调用者知道为什么会一直错误。
这似乎是一个矛盾的要求。然而最终我想到了一个办法:让重试一直进行下去,谁需要关心异常谁就去 catch
异常,不需要关心异常的模块则跟着一直重试直到成功。
我们通过编写一个自己的 Awaiter 来实现,本文将说明其思路和最终实现的代码。
本文内容
Awaiter 系列文章
入门篇:
- .NET 中什么样的类是可使用 await 异步等待的?
- 定义一组抽象的 Awaiter 的实现接口,你下次写自己的 await 可等待对象时将更加方便
- .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
实战篇:
遇到了什么问题
有一个任务,可能会出错,然而重试有可能可以解决。典型的例子是写入文件,你可能因为其他进程占用的问题而导致无法写入,然而一段时间之后重试是可以解决的。
现在,不同业务对这同一个操作有不同的需求:
- 有的业务不关心写入结果到底如何
- 有的业务由于时间有限,只能接受几次的重试
- 有的业务关心写入过程中的异常
- 而有的业务非常闲,只要一直写入就行了,最终成功告诉我就好
可是,我们如何在一个任务中同时对所有不同的业务需求进行不同种类的响应呢?
思路
我的思路是:
- 当有业务发起请求之后,就开启一个不断重试的任务;
- 针对这个请求的业务,返回一个专为此业务定制的可等待对象;
- 如果在重试完成之前,还有新的业务请求发起,那么则返回一个专为此新业务定制的可等待对象;
- 一旦重试任务成功完成,那么所有的可等待对象强制返回成功;
- 而如果重试中有的可等待对象已经等待结束但任务依旧没有成功,则在可等待对象中引发任务重试过程中发生过的异常。
这样,任务不断重试。而且,无论多少个业务请求到来,都只是加入到循环中的一部分来,不会开启新的循环任务。每个业务的等待时长和异常处理都是自己的可等待对象中处理的,不影响循环任务的继续执行。
关于源代码说明
本文所述的所有源代码可以在 https://gist.github.com/walterlv/d2aecd02dfad74279713112d44bcd358 查看和下载到最新版本。
期望如何使用这个新的 Awaiter
public class WalterlvDemo
{
// 记录一个可以重试的循环。
private readonly PartialAwaitableRetry _loop;
public WalterlvDemo