Dart多个future队列完成加入顺序关系及原子性论证

目录
  • 引言
  • 什么是 Future
  • Future 操作具备'原子性'吗
    • 实验写法一:
    • 实验写法二:
    • 实验写法三:
  • 论证结论

引言

Dart 是一个在单线程中运行的程序。在程序中执行一个需要长时间的执行的操作,为避免卡住UI主线程,我们会用到异步(future),可以使程序在等待一个耗时操作完成时继续处理其他工作。

在进入正题之前,我们先了解一下 Dart 的消息循环机制:

  • Dart 从两个队列执行任务:Event事件队列Microtask微任务队列
  • 事件循环会优先处理微任务队列,microtask清空之后才将 event事件队列中的下一个项目出队并处理
  • 事件队列具有来自Dart(Future,Timer,Isolate Message等)和系统(用户输入,I/O等)
  • 微任务队列目前仅包含来自Dart,当然我们也可以自己往微队列中插入任务

什么是 Future

Future 是什么?这里我们用小篇幅简单描述一下。future是一个异步的执行操作,可以在不阻塞代码的情况下实现耗时功能。

main() {
  Future(() {
    print('我是一个耗时操作');
  }).then((value){
    print('future 结束了');
  });
  print('main');
}

//打印结果
main
我是一个耗时操作
future 结束了

在项目中,我们使用 FutureAsyncawait 相互组合实现异步编码。

Future 操作具备'原子性'吗

上面的篇幅我们说过,future 创建后会被直接加入到事件队列依次执行。那么在上一个 future 在没有标识完成前,下一个 future 可以被执行吗?

小编写了几个样例来做实验:

实验写法一:

main() {
  Test1.future1().then((value) => print(value));
  Test1.future2().then((value) => print(value));
}
abstract class Test1 {
  static Future<String> future1() async {
    return Future(() async {
      print('开始 future1');
      await TestTool.timeConsume(1000000000); //耗时运算
      print('一千年过去了');
      return 'future1 结束了';
    });
  }
  static Future<String> future2() async {
    return Future(() async {
      print('开始 future2');
      await TestTool.timeConsume(1000); //耗时运算
      print('继续 future2');
      return 'future2 结束了';
    });
  }
}

//打印结果
开始 future1
一千年过去了
future1 结束了
开始 future2
继续 future2
future2 结束了

实验结果:

  • 从打印结果上看,future任务没有中断,执行完当前任务后才可执行队列中的下一个future

实验写法二:

main() {
  Test2.future1().then((value) => print(value));
  Test2.future2().then((value) => print(value));
}
abstract class Test2 {
  static Future<String> future1() async {
    print('开始 future1');
    await TestTool.timeConsume(1000000000);//耗时运算
    print('一千年过去了');
    return 'future1 结束了';
  }
  static Future<String> future2() async {
    print('开始 future2');
    await TestTool.timeConsume(1000);//耗时运算
    print('继续 future2');
    return 'future2 结束了';
  }
}

//打印结果
开始 future1
开始 future2
一千年过去了
future1 结束了
继续 future2
future2 结束了

实验结果:

  • future2future1没有结束前就已经开始了任务。
  • future2会在future1任务执行完成后响应结束,整个过程仍然保持了完成顺序与加入事件队列的顺序一致性。

实验写法三:

main() {
  Test3.future1().then((value) => print(value));
  Test3.future2().then((value) => print(value));
}
abstract class Test3 {
  static Future<String> future1() async {
    print('开始 future1');
    await Future(() => TestTool.timeConsume(1000000000));//耗时运算
    print('一千年过去了');
    return 'future1 结束了';
  }
  static Future<String> future2() async {
    print('开始 future2');
    await TestTool.timeConsume(1000);//耗时运算
    print('继续 future2');
    return 'future2 结束了';
  }
}

//打印结果
开始 future1
开始 future2
继续 future2
future2 结束了
一千年过去了
future1 结束了

实验结果:

  • 从打印结果上看,future1开始后,future2直接开始任务,且future2任务完成后直接标识完成。
  • future1future2 的完成顺序已经和加入事件队列的顺序无关了,只与内部耗时正相关。

附上耗时代码:

abstract class TestTool {
  ///耗时操作
  static Future<int> timeConsume(int num) async {
    final result = _timeConsume(num);
    return result;
  }
  static int _timeConsume(int num) {
    int count = 0;
    while (num > 0) {
      if (num % 2 == 0) {
        count++;
      }
      num--;
    }
    return count;
  }
}

论证结论

综合上述三种写法分析:

future方法体内部不属于可靠的'原子性操作',不同的写法有不同的差异性。 如果想将整个方法体内部作为不可拆分的执行单位。在外层使用Future进行包裹处理,如写法一中Test1示例:

static Future<T> funcName() async {
  return Future(() async {
    ...
    具体的方法体内容
    ...
    return result;
  });
}

future在创建的同时,就会被加入到event事件队列中。事件队列是依次执行的,但每个future的完成顺序与加入的顺序不存在可靠的一致性。 如果在业务内想保持顺序的一致性,可参考上述写法,或使用 await 进行强制等待如:

main() async {
  await Test2.future1().then((value) => print(value));
  Test2.future2().then((value) => print(value));
}

这样写,future2 就一定会在 future1 执行完成后才进入开始状态。

以上就是Dart多个future队列完成加入顺序关系及原子性论证的详细内容,更多关于Dart多个future原子性的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解Flutter和Dart取消Future的三种方法

    目录 使用异步包(推荐) 完整示例 使用 timeout() 方法 快速示例 将Future转换为流 快速示例 结论 使用异步包(推荐) async包由 Dart 编程语言的作者开发和发布.它提供了dart:async风格的实用程序来增强异步计算.可以帮助我们取消Future的是CancelableOperation类: var myCancelableFuture = CancelableOperation.fromFuture( Future<T> inner, { FutureOr on

  • ExecutorService Callable Future多线程返回结果原理解析

    目录 正文 简单例子 异步执行内部实现run方法逻辑 FutureTask的run方法 set(result)方法 正文 在并发多线程场景下,存在需要获取各线程的异步执行结果,这时,就可以通过ExecutorService线程池结合Callable.Future来实现. 简单例子 public class ExecutorTest { public static void main(String[] args) throws ExecutionException, InterruptedExce

  • Python并发编程之未来模块Futures

    目录 区分并发和并行 并发编程之Futures 到底什么是Futures? 为什么多线程每次只有一个线程执行? 总结 不论是哪一种语言,并发编程都是一项非常重要的技巧.比如我们上一章用的爬虫,就被广泛用在工业的各个领域.我们每天在各个网站.App上获取的新闻信息,很大一部分都是通过并发编程版本的爬虫获得的. 正确并合理的使用并发编程,无疑会给我们的程序带来极大性能上的提升.今天我们就一起学习Python中的并发编程——Futures. 区分并发和并行 我们在学习并发编程时,常常会听到两个词:并发

  • Java CompletableFuture实现多线程异步编排

    目录 一 :问题背景 二 :CompletableFuture介绍 三 :具体场景 1.0 单个任务 1.0.1 runAsync:无返回值 1.0.2 supplyAsync:有返回值 1.0.3 supplyAsync:有返回值 2.0 两个任务编排 2.0.1 thenRunAsync 2.0.2 thenAcceptAsync 2.0.3 thenApplyAsync 3.0 三任务编排 3.0.1 三任务组合 3.0.2 三任务组合二 4.0 多任务的编排 4.0.1.allOf:所有

  • CompletableFuture 异步编排示例详解

    目录 从Future聊起 CompletableFuture 创建异步任务 异步回调 异步编排 串行 AND OR Future 机制扩展 CompletableFuture 实践 从Future聊起 Future是java 1.5引入的异步编程api,它表示一个异步计算结果,提供了获取异步结果的能力,解决了多线程场景下Runnable线程任务无法获取结果的问题. 但是其获取异步结果的方式并不够优雅,我们必须使用Future.get的方式阻塞调用线程,或者使用轮询方式判断 Future.isDo

  • Future与FutureTask接口实现示例详解

    目录 正文 Future类 FutureTask Callable+Future获取执行结果 Callable+FutureTask获取执行结果 正文 Future就是对于具体的Runnable或者Callable任务的执行结果进行取消.查询是否完成.获取结果等操作.必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果. Future类 Future类位于java.util.concurrent包下,它是一个接口: /** * @see FutureTask * @see Exec

  • Dart多个future队列完成加入顺序关系及原子性论证

    目录 引言 什么是 Future Future 操作具备'原子性'吗 实验写法一: 实验写法二: 实验写法三: 论证结论 引言 Dart 是一个在单线程中运行的程序.在程序中执行一个需要长时间的执行的操作,为避免卡住UI主线程,我们会用到异步(future),可以使程序在等待一个耗时操作完成时继续处理其他工作. 在进入正题之前,我们先了解一下 Dart 的消息循环机制: Dart 从两个队列执行任务:Event事件队列 和 Microtask微任务队列 事件循环会优先处理微任务队列,microt

  • Rust 实现 async/await的详细代码

    目录 Future Wake & Context 为什么需要 executor ? 什么是 waker ? async/await Executor Waker struct 到 ArcWake trait FuturesUnordered 单线程 executor 线程池 executor 总结 异步编程在 Rust 中的地位非常高,很多 crate 尤其是多IO操作的都使用了 async/await. 首先弄清楚异步编程的几个基本概念: Future Future 代表一个可在未来某个时候获

  • Flutter框架解决盒约束widget和assets里加载资产技术

    目录 盒约束 文本输入widget assets 加载资产 盒约束 flutter: assets: - assets/my_icon.png - assets/background.png 在Flutter中,小部件由其底层RenderBox对象渲染.渲染框受其父对象的约束,并在这些约束下调整自身大小.约束包括最小宽度.最大宽度和高度:尺寸由特定的宽度和高度组成. 通常,根据小部件如何处理其约束,有三种类型的框: 尽可能大.例如,“Center”和“ListView”的渲染框 遵循子部件的大小

  • Golang控制协程执行顺序方法详解

    目录 循环控制 通道控制 互斥锁 async.Mutex 在 Go 里面的协程执行实际上默认是没有严格的先后顺序的.由于 Go 语言 GPM 模型的设计理念,真正执行实际工作的实际上是 GPM 中的 M(machine) 执行器,而我们的协程任务 G(goroutine) 协程需要被 P(produce) 关联到某个 M 上才能被执行.而每一个 P 都有一个私有队列,除此之外所有的 P 还共用一个公共队列.因此当我们创建了一个协程之后,并不是立即执行,而是进入队列等待被分配,且不同队列之间没有顺

  • Flutter图片与文件选择器使用实例

    目录 引言 一.image_picker 1.安装 2.使用 3.属性 4.注意 二.flutter_document_picker 1.安装 2.使用 总结 引言 我已经一个多星期没碰过电脑了,今日上班,打开电脑的第一件事就是想着写点什么.反正大家都还沉浸在节后的喜悦中,还没进入工作状态,与其浪费时间,不如做些更有意义的事情. 今天就跟大家简单分享一下Flutter开发过程中经常会用到的图片和文件选择器. 一.image_picker 一个适用于iOS和Android的Flutter插件,能够

  • flutter中的资源和图片加载示例详解

    目录 封面图 指定相应的资源 资源绑定 Asset bundling 资源变体 加载资源 加载文本资源 加载图片 加载依赖包中的图片 最后 封面图 下个季度的目标是把前端监控相关的内容梳理出来,梳理出来之后可能会在公司内部做个分享- Flutter应用程序既括代码也包括一些其他的资产,我们通常这些资产为资源. 有时候我会思考assets这个单词,在程序中到底应该翻译为资产呢?还是翻译为资源?按照习惯,我们这里还是称为资源好了- 这些资源是一些与应用程序捆绑在一起和并且部署应用时会用到的的文件,在

  • Bloc事件流是一个阻塞队列结论解析

    前言 在Flutter中有state的概念,我们使用Bloc进行状态管理,通过Bloc.addEvent的方式进行事件传递,状态变更.关于Bloc的基础用法,可以查阅Bloc官网相关资料,这里我们仅记录一下Bloc的队列等待. 新建一个Bloc类 class TestBloc extends Bloc<TestEvent, TestState> { TestBloc() : super(new TestState()); @override Stream<TestState> ma

  • 简析Java中的util.concurrent.Future接口

    在一个单线程应用中,当你调用一个方法只有计算结束才会返回结果( IOUtils.toString()  comes from Apache Commons IO ): public String downloadContents(URL url) throws IOException { try(InputStream input = url.openStream()) { return IOUtils.toString(input, StandardCharsets.UTF_8); } } /

随机推荐