异步任务中的重新进入(Reentrancy)

异步任务中的重新进入(Reentrancy)

2017-12-05 14:10

一个按钮,点击执行一个任务。我们可能直接在它的 Click 事件中写下了执行任务的代码。

一般我们无需担心这样的代码会出现什么问题——但是,这样的好事情只对同步任务有效;一旦进入了异步世界,这便是无尽的 BUG!


重新进入(Reentrancy)

private void Button_Click(object sender, RoutedEventArgs e)
{
    DoSomething();
}

private void DoSomething()
{
    // 同步任务。
}

▲ 以上,在按钮点击事件中执行同步任务

上面的代码,无论我们在界面上多么疯狂地点击按钮,因为 UI 会在任务执行的过程中停止响应,所以 DoSomething 只会依次执行(还会偶尔忽略一些)。这通常不会造成什么问题,但如果 DoSomething 变成异步的 DoSomethingAsync(就像下面那样),那么情况就变得不同了。

private async void Button_Click(object sender, RoutedEventArgs e)
{
    await DoSomethingAsync();
}

private async Task DoSomethingAsync()
{
    // 异步任务。
}

▲ 以上,在按钮点击事件中执行异步任务

由于任务执行的过程中 UI 依然是响应的,DoSomethingAsync 会因此在每一次点击的时候都进入。在异步任务结束之前重新进入此异步任务的过程,叫做重新进入(Reentrancy)。

重新进入的五种方式

微软在 Handling Reentrancy in Async Apps (C#) 一文中给出了重新进入的三种方式:

  1. 禁用“开始”按钮
  2. 取消和重启操作
  3. 运行多个操作并将输出排入队列

从语言描述中就能知道除了第 2 点看起来具有通用性外,其他两点只为了解决文章中面临的“输出网页列表”问题。第 1 点其思想可以重用,但第 3 点就很难抽取公共的重新进入思想。于是,我总结其前两点,再额外补充两种重新进入的方式,和不处理一起作为五种不同的处理方法。

  • 禁用重新进入
  • 并发
  • 取消然后重启操作
  • 将异步任务放入队列中依次执行
  • 仅执行第一次和最后一次

禁用重新进入

禁用是最直接最简单也最彻底的重新进入问题解决办法。

Button.IsEnabled = false;
await DoSomethingAsync();
Button.IsEnabled = true;

既然重新进入可能出问题,那我们就禁止重新进入好了……

并发

当然,不处理也是一种方法。这意味着我们需要真的考虑 DoSomethingAsync 并发造成的影响。

取消然后重启操作

取消,然后重新执行一次,这也是常见的重新进入类型。浏览器或者资讯类 APP 中的刷新功能就是这种重新进入方式最常见的应用场景,用户重新执行一次刷新,可能因为前面那一次(因为网络问题或其他原因)太慢,所以重新开始。

将异步任务放入队列中依次执行

放入队列中是因为此异步任务的顺序是很重要的,要求每一次执行且保持顺序一致。典型的应用场景是每一次执行都需要获取或生成一组数据输出(到屏幕、文件或者其他地方)。

仅执行第一次和最后一次

如果用户每一次执行此异步任务都会获取当前应用程序的最新状态,然后根据最新状态执行;那么如果状态更新了,对旧状态执行多少次都是浪费的。

比如保存文件的操作。第一次进入异步任务的时候会进行保存,如果保存过程没有结束又触发新的保存,则等上一次保存结束之后再执行保存操作即可。而如果第一次保存没有结束的时候又触发非常多次的保存,也只需要在第一次结束之后再保存一次即可,毕竟既然最后一次保存时的状态已经是最新状态,不需要再把之前旧的状态保存一次。


参考资料

本文会经常更新,请阅读原文: https://walterlv.com/post/reentrancy-in-async-method.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 (walter.lv@qq.com)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏PHP技术大全

grafana+ prometheus+php 监控系统实践

团队在开发流媒体服务,需要实现一个监控在线人数的功能,可以看到历史有多少人在线,当前有多少人在线的功能。 如果用mysql等关系型数据库来实现,可以用事件记录日...

32230
来自专栏Seebug漏洞平台

从 CVE-2018-8495 看 PC 端 url scheme 的安全问题

本文受 CVE-2018-8495 漏洞的启发,以学习的目的,针对 PC 端 url scheme 的安全问题进行了分析研究。

13910
来自专栏全华班

工作流学习-使用eclipse流程开发

阅读文本大概需要 5 分钟。 我们前文中介绍了Activiti,知道它是目前市面上比较流行的工作流框架。同时也简单指导大家从头开始搭建一下Activit...

40840
来自专栏岑志军的专栏

fastlane实现自动化打包

21820
来自专栏進无尽的文章

Fastlane| 一句代码完成自动打包发布到蒲公英

第一个选项的意思是:自动截屏。这个功能能帮我们自动截取APP中的截图,并添加手机边框(如果需要的话) 第二个选项的意思是:自动发布beta版本用于TestFli...

20230
来自专栏逸鹏说道

数据库备份相关

汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql ADO.Net简单演示:https://gith...

39590
来自专栏我叫刘半仙

原帮你摆脱鼠标,提高工作效率的免费小工具

       一款只用键盘操作就能打开想要的软件的神器--ALTRun。在我刚开始学编程时,一次偶然的机会接触了ALTRun后,实在是爱不释手。它是一款快速启动...

44360
来自专栏自由而无用的灵魂的碎碎念

Windows Server 2008 R2 到Windows 7的改造之路

与windows 7相比,windows server 2008 r2功能更为全面,开发人员也更喜欢使用win server 2008 r2进行开发。

18030
来自专栏容器云生态

使用系统内置script和scriptreplay命令来记录操作记录

想要记录整个操作流程,需要使用到两个工具,script和scriptreplay,实验环境是CentOS6.6,默认都是安装的! script命令用来记录整个历...

30470
来自专栏小黄人打代码

这些工具都是你需要的

17640

扫码关注云+社区

领取腾讯云代金券