一个按钮,点击执行一个任务。我们可能直接在它的 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 中的刷新功能就是这种重新进入方式最常见的应用场景,用户重新执行一次刷新,可能因为前面那一次(因为网络问题或其他原因)太慢,所以重新开始。

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

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

仅执行第一次和最后一次

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

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


参考资料

异步任务中的重新进入(Reentrancy)的更多相关文章

  1. 你不知道的this—JS异步编程中的this

    Javascript小学生都知道了javascript中的函数调用时会 隐性的接收两个附加的参数:this和arguments.参数this在javascript编程中占据中非常重要的地位,它的值取决 ...

  2. Android异步回调中的UI同步性问题

    Android程序编码过程中,回调无处不在.从最常见的Activity生命周期回调开始,到BroadcastReceiver.Service以及Sqlite等.Activity.BroadcastRe ...

  3. [Effective JavaScript 笔记]第62条:在异步序列中使用嵌套或命名的回调函数

    异步程序的操作顺序 61条讲述了异步API如何执行潜在的代价高昂的I/O操作,而不阻塞应用程序继续处理其他输入.理解异步程序的操作顺序刚开始有点混乱.例如,下面的代码会在打印"finishe ...

  4. [置顶] Ajax程序:处理异步调用中的异常(使用Asp.Net Ajax内建的异常处理方法)

    无论在Window应用程序,还是Web应用程序以对用户友好的方式显示运行时的异常都是很有必要,尤其对于可能有很多不确定因素导致异常的Web应用程序;在传统的Web开发中,处理异常的方式——设计专门一个 ...

  5. 使用domain模块捕获异步回调中的异常

    和其他服务器端语言相比,貌似node.js 对于异常捕捉确实非常困难. 首先你会想到try/catch ,但是在使用过程中我们会发现并没有真正将错误控制在try/catch 语句中. 为什么? 答案是 ...

  6. 异步请求中jetty处理ServletRequestListener的坑

    标题起得比较诡异,其实并不是坑,而是jetty似乎压根就没做对异步request的ServletRequestListener的特殊处理,如果文中有错误欢迎提出,可能自己有所疏漏了. 之前遇到了一个b ...

  7. Effective JavaScript Item 63 注意异步调用中可能会被忽略的异常

    异常处理是异步编程的一个难点. 在同步的代码中,异常可以非常easy地通过try catch语句来完毕: try { f(); g(); h(); } catch (e) { // handle an ...

  8. 【转】C# Async/Await 异步编程中的最佳做法

    Async/Await 异步编程中的最佳做法 Stephen Cleary 近日来,涌现了许多关于 Microsoft .NET Framework 4.5 中新增了对 async 和 await 支 ...

  9. [知识库:python-tornado]异步调用中的上下文控制Tornado stack context

    异步调用中的上下文控制Tornado stack context https://www.zouyesheng.com/context-in-async-env.html 这篇文章真心不错, 非常透彻 ...

随机推荐

  1. 监控控制台是否运行的bat

    @echo offrem set secs=5set srvname="TRS.Export.Scheduler.exe" echo.echo ================== ...

  2. AtCoder Grand Round 012B Splatter Painting

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  3. transform对定位元素的影响

    1.温故知新 absolute:生成绝对定位的元素,相对于除position:static 定位以外的第一个有定位属性的父元素进行定位,若父元素没有定位属性则相对于浏览器窗口的左上角定位,定位的元素不 ...

  4. nyoj20——有向无环图深搜模板

    吝啬的国度 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 在一个吝啬的国度里有N个城市,这N个城市间只有N-1条路把这个N个城市连接起来.现在,Tom在第S号城市, ...

  5. spring boot 中logback多环境配置

    spring boot 配置logback spring boot自带了log打印功能,使用的是Commons logging 具体可以参考spring boot log 因此,我们只需要在resou ...

  6. NPOI:操作总结

    1.套路 使用了NPOI一段时间,也慢慢了解了操作的流程,或者说套路: a.创建Workbook: HSSFWorkbook Workbook = new HSSFWorkbook(); b.在Wor ...

  7. JDK的BIO, NIO, AIO

    背景知识点我 1. BIO JDK5之前, JDK的IO模式只有BIO(同步阻塞)问题: 因为阻塞的存在, 需对每个请求开启一个线程. 过多的线程切换影响操作系统性能解决: 使用线程池, 处理不过来的 ...

  8. mysql主从复制跳过错误

    mysql主从复制,经常会遇到错误而导致slave端复制中断,这个时候一般就需要人工干预,跳过错误才能继续 跳过错误有两种方式: 1.跳过指定数量的事务: mysql>slave stop; m ...

  9. poj21516

    首先对于一种商品 如果这种货不足需求就直接输出-1 剩下的就是KM算法 分k次分别计算每种商品的最小权值匹配 代码: #include<cstdio> #include<cstrin ...

  10. cf 915

    t1:2分钟ac,简单模拟 t2:3发wa,最后再10分钟的时候过了 但是最后被hack了 t3:2发wa,最后还是被hack了 t4:拓扑排序 然后将一个点入度减一 然后是否能拓扑 t5:离散化+线 ...