flutter单线程异步及isolate使用过程遇到的问题(代码片段)

一叶飘舟 一叶飘舟     2022-10-23     242

关键词:

遇到两个问题:

  1. 官方 子 isolate 内不能使用插件. 解决方案: 使用 isolate_handler / flutter_isolate 代替官方 isolate 参考链接
  2. 子 isolate 处理完耗时操作, 传递结果给主 isolate 的方法: void send(Object? message); 耗时严重, 达秒级, 顿了一下, 页面才刷新的感觉( 未解决 )

Dart 单线程异步怎么实现的?

Dart 是单线程语言. Flutter 依赖于Dart
Dart的 “线程” 是指 isolate.
isolate 与线程的区别: isolate 之间是内存独立的, isolateA 不能访问isolateB的内存数据.
Future 可以实现异步.异步但串行非并发执行任务
单线程并不是说 Dart 只有一个线程! Dart可以创建子 isolate 实现多线程.


loop内有两个FIFO队列

  1. microask queue
  2. event queue (里面就是Future任务)

前者执行优先级高于后者, 如图


future / async / await

 官方async-await描述

1. future 是Future 的实例对象, future 代表一个异步操作的结果, 它有两种状态: 未完成和已完成.
2. await 可以修饰函数/, 如果该函数返回值为Future 类型, 则会阻塞当前流程, 等待被修饰的函数执行完成后继续执行后续代码. 函数内使用 await 的前提是该函数是一个 async 函数. 也就是说 await 要和 async 配对使用.
3. 但是 async 函数内可以没有 await 关键字.

总结: await future 会阻塞流程

一: 关于async 函数:

函数内有阻塞流程(await future)的操作, 则函数会返回一个 future 实例 
(1) 如果该函数本身有返回值, 比如类型为T, 则返回类型为Future<T.>
(2) 如果该函数本身没有返回值, 则返回类型为 Future<void.>

比如下面的 funcTest 函数会返回 Future<void.>

funcTest() async 
	await Future(()  
    	print('funcTest end');
 	 );

二 async 和 await 的执行流程

一个async函数同步运行直到第一个 await关键字。这意味着在async函数体内,第一个await关键字之前的所有同步代码都会立即执行。

版本说明: 在 Dart 2.0 之前,async函数立即返回,不执行async函数体内的任何代码。

例子:

void main() async 
	print('main begin'); 
	funcB();
	print('main end'); 

funcB() async 
	print('funcB begin'); 
	await Future.delayed(Duration(seconds: 2), ()
		print('funcB delayed over'); 
	); 
	print('funcB end'); 

funcB 虽然是异步函数, 但是在main函数执行到funcB时并不会跳过去打印’main end’, 而是继续执行funcB , 直到遇到funcB的第一个await关键字后返回.

打印结果:
 

main begin
funcB begin
main end
funcB delayed over
funcB end

Future 实现异步

官方event-loop描述

1. 立刻把任务加入 event queue, 用 Future():
// Adds a task to the event queue.
new Future(() 
// …code goes here…
);
2. some time 后把任务加入(并非执行!) event queue用 Future.delayed():
// After a one-second delay, adds a task to the event queue.
new Future.delayed(const Duration(seconds:1), () 
// …code goes here…
);

注意: Future 或 Future.delayed 的返回值是Future<T.>类型还是 Future<void.> 类型, 取决于执行函数有无返回值, 有返回值就是前者, 无返回值就是后者; 可以把执行函数理解成上文的异步函数
执行main函数:

  1. main begin
  2. funcA 是同步函数, 正常执行
    1. funcA
  3. 这时到了funcB, 进入函数内同步执行, 直到遇到第一个await返回. 同时把函数内Future.delay添加到 event queue 队尾. 并立刻往下执行 funcC
    1. funcB begin
  4. funcC 是 async 函数, 虽然调用时使用了await 关键字, 但是 funcC 函数内部没有阻塞流程的代码, 不会返回future. 这里 await funcC(); 等同于 funcC();
    1. funcC begin
    2. 2s后把执行函数加入 event loop
    3.  funcC end
  5. 5. funcD 与 funcC 不同的是, 方法内的 Future.delayed 调用时使用了 await, 则funcD 返回一个 future, 且会阻塞funcD 函数体, 等Future.delayed 执行函数完成才会打印 ‘funcD end’,又因为 main 函数 await funcD(); 所以要等 funcD 函数全部执行完成, 才会打印 'main end’
    1. funcD begin
    2. 2s后把执行函数加入 event loop
    3. Future.delay 执行函数完成 打印 funcD end
  6. main 函数处于等待 funcD的状态, 开始loop, 因为我们没有往microtask queue 添加任务, 所以串行执行(非并发!!!) event queue
    1. 此时 event queue 的任务:  
      funcB.Future.delayed
      funcC.Future.delayed
      funcD.Future.delayed

FIFO原则

(1) funcB delayed over
(2) funcB end 这里我的理解是, ‘funcB end’ 被放在了’funcB delayed over’的 then 回调里, ‘funcB delayed over’ 完成后立刻执行 ‘funcB end’ 然后才 loop下一个任务
(3) funcC delayed over
(4) funcD delayed over
(5) mainEnd

void main() async 
	print('main begin'); 
	funcA();
	funcB();
	await funcC();
	await funcD();
	print('main end'); 

funcA() 
	print('funcA'); 

funcB() async 
	print('funcB begin'); 
	// 2s后把执行函数加入 event loop, 且执行函数变成完成状态才会执行下面的 'funcB end'
	await Future.delayed(Duration(seconds: 2), ()
		print('funcB delayed over'); 
	); 
	print('funcB end'); 

funcC() async 
	print('funcC begin'); 
	// 2s后把执行函数加入 event loop
	Future.delayed(Duration(seconds: 2), ()
		print('funcC delayed over'); 
	); 
	print('funcC end'); 

funcD() async 
	print('funcD begin'); 
	// 2s后把执行函数加入 event loop, 且执行函数变成完成状态才会执行下面的 'funcD end'
	await Future.delayed(Duration(seconds: 2), ()
		print('funcD delayed over'); 
	); 
	print('funcD end'); 

isolate

我们平时写的代码就运行在flutter创建好的UI线程. 当运行耗时代码时页面会有卡顿感, 就是掉帧了.
解决方案: 开辟新线程处理耗时操作, 把处理结果传给 UI 线程刷新页面.
Dart 开辟新线程的方式是使用 isolate.
isolate 与普通线程的区别在于: isolate之间是内存隔离的!!!.
比如: isolate A 内不能访问 isolate B 的变量

此时好像就结束了, 但是! 官方isolate有一个致命局限性…
我们自己创建的isolate内是没办法使用插件的. 原因是Platform-Channel的通信只能在主isolate内执行
在Stack Overflow 发现两个插件, 可以完美解决这个问题:

  1. isolate_handler
  2. flutter_isolate

但是当我感觉终于要看到胜利的曙光时我发现,
这个方法耗时达秒级(把子isolate执行完毕的结果发送给主isolate)

abstract class SendPort implements Capability 
  /// Sends an asynchronous [message] through this send port, to its
  /// corresponding `ReceivePort`.
  ///
  /// The content of [message] can be: primitive values
  /// (null, num, bool, double, String), instances of [SendPort],
  /// and lists and maps whose elements are any of these.
  /// List and maps are also allowed to contain cyclic references.
  ///
  /// In the special circumstances when two isolates share the same code and are
  /// running in the same process (e.g. isolates created via [Isolate.spawn]),
  /// it is also possible to send object instances (which would be copied in the
  /// process). This is currently only supported by the
  /// [Dart Native](https://dart.dev/platforms#dart-native-vm-jit-and-aot)
  /// platform.
  ///
  /// The send happens immediately and doesn't block.  The corresponding receive
  /// port can receive the message as soon as its isolate's event loop is ready
  /// to deliver it, independently of what the sending isolate is doing.
  
	void send(Object? message);


如果这样, isolate的使用场景就变得很少了, 也只能后台任务不需要刷新页面的情况下使用了

参考: futures-isolates-event-loop
 

flutter之异步操作async原理future本质(代码片段)

目录Dart单线程语言eventloop工作原理Future async &await参考资料: ​​​​​​​Dart单线程语言Dart是单线程执行模型,但是它支持 Isolate(一种让Dart代码运行在其他线程的方式)、事件循环和异步编程。除非你... 查看详情

flutter之异步操作async原理future本质(代码片段)

目录Dart单线程语言eventloop工作原理Future async &await参考资料: ​​​​​​​Dart单线程语言Dart是单线程执行模型,但是它支持 Isolate(一种让Dart代码运行在其他线程的方式)、事件循环和异步编程。除非你... 查看详情

flutter之多线程

...他线程。Future、scheduleMicrotask(微任务)、Isolate、ComputeFlutter的loop优先级讲解主线程任务优先执行>scheduleMicrotask(微任务)(其他微任务)>Future-->当前Future的then等回调>其他Future-->其他Future严格来讲Isolate、compute(对I... 查看详情

异步编程之isolate

...支持单个和多个isolate中的异步。1.为什么需要isolate在Dart/Flutter应用程序启动时,会启动一个主线程其实也就是RootIsolate,在RootIsolate内部运行一个EventLoop事件循环。所以所有的Dart代码都是运行在Isolate之中的,它就像是机器上的... 查看详情

dart异步编程之isolate和事件循环(代码片段)

...务以及其他特性用于编写现代异步程序以及响应式程序(Flutter)。本文讲的是Dart后台任务的基础:Isolate和事件循环。我们先从Isolate开始。Isolates大多数应用程序中,线程的数量都不止一个。多个线程可以互不干扰地并发执... 查看详情

flutter之异步操作async原理future本质(代码片段)

...则你的Dart代码永远运行在UI线程,并由eventloop驱动。Flutter的eventloop和iOS中的mainloop相似——Looper 是附加在主线程上的。eventloop工作原理eventloop循环机制是和eventqueue、 microtaskqueue这两个队列一起工作的。工作流程可以形象为... 查看详情

flutter入门之dart中的并发编程异步和事件驱动详解(代码片段)

并发编程我们知道dart是个单线程的语言,和js一样,所以dart中不存在多线程操作,那么我们如果遇到多任务并行的场景,该如何去做呢?dart中提供了一个类似于java新线程但不能共享内存的独立运行的workerÿ... 查看详情

Dart / Flutter:Isolate ***函数的异步行为

】Dart/Flutter:Isolate***函数的异步行为【英文标题】:Dart/Flutter:asyncbehaviourofanIsolate\'stoplevelfunction【发布时间】:2019-08-2911:17:01【问题描述】:好人好人,我遇到了一种奇怪的行为当异步使用隔离的***函数时;你可以找到示例代... 查看详情

异步编程之isolate

...说到了Dart中的EventLoop(事件循环)。我们知道了Dart是单线程模型,也就是实现异步需要借助EventLoop来进行事件驱动。所以Dart只有一个主线程,其实在Dart中并不是叫Thread,而是有个专门名词叫isolate(隔离)。其实在Dart也会遇到... 查看详情

dart语言中的isolate(代码片段)

...言在设计上很多方面都借鉴了JS。例如JS的面向对象、单线程、事件循环等。同时也做了许多优化,比如JavaScript低效的解释执行,而Dart可以在运行前直接编译为机器码,提高了执行效率,这个过程叫做AOT(Ahead... 查看详情

dart语言中的isolate(代码片段)

...言在设计上很多方面都借鉴了JS。例如JS的面向对象、单线程、事件循环等。同时也做了许多优化,比如JavaScript低效的解释执行,而Dart可以在运行前直接编译为机器码,提高了执行效率,这个过程叫做AOT(Ahead... 查看详情

flutter入门之dart中的并发编程异步和事件驱动详解(代码片段)

并发编程我们知道dart是个单线程的语言,和js一样,所以dart中不存在多线程操作,那么我们如果遇到多任务并行的场景,该如何去做呢?dart中提供了一个类似于java新线程但不能共享内存的独立运行的workerÿ... 查看详情

异步编程之eventloop

...,我们将正式进入Dart中的异步编程,异步编程属于Dart、Flutter开发中的重点和难点。这一系列文章不仅仅需要学会如何使用Dart中异步API,更重要的是需要去理解其背后的原理。我们都知道,Dart语言和Javascript一样都是单线程模型... 查看详情

flutter异步编程-捌|计算耗时?isolate来帮忙(代码片段)

一、问题引入-计算密集型任务假如现在有个需求,我想要计算1亿个1~10000间随机数的平均值,在界面上显示结果,该怎么办?可能有小伙伴踊跃发言:这还不简单,生成1亿个随机数,算呗。1.搭建测试场景... 查看详情

flutter的异步回调(代码片段)

一、Dart的事件循环        Dart的事件循环机制相当于Android中的handler机制,Android中是多线程了,但是dart是单线程的,更像js中的事件循环机制,虽然是单线程的,但是dart也提供了异步的功能函数对象Future&... 查看详情

spring使用threadpooltaskexecutor自定义线程池及实现异步调用

多线程一直是工作或面试过程中的高频知识点,今天给大家分享一下使用ThreadPoolTaskExecutor来自定义线程池和实现异步调用多线程。一、ThreadPoolTaskExecutor本文采用Executors的工厂方法进行配置。1、将线程池用到的参数定义到配置文... 查看详情

javascript异步编程学习及实例(代码片段)

...这种异步编程尤其重要并大量存在,很大的原因是js为单线程模式,其中包含了ui刷新和响应。想像一下,如果我们写了一段js代码,他会发起ajax请求,如果为同步模式,就意味着这时js单线程一直等待 查看详情

同步异步的使用场景及好处

...时操作,因为比较影响客户体验和使用性能5、不影响主线程逻辑 同步的使用场景:不使用异步的时候 同步的好处:1、同步流程对结果处理通常更为简单,可以就近处理。2、同步流程对结果的处理始终和前文保持在一个... 查看详情