Flutter开发指南之理论篇:Dart语法05(单线程模型,事件循环模型,Isolate)

Stella981
• 阅读 609

此文转载自:https://blog.csdn.net/AndrExpert/article/details/110823218

总目录

Flutter开发指南之理论篇:Dart语法01(数据类型,变量,函数)
Flutter开发指南之理论篇:Dart语法02(运算符,循环,异常)
Flutter开发指南之理论篇:Dart语法03(类,泛型)
Flutter开发指南之理论篇:Dart语法04(库,异步,正则表达式)
Flutter开发指南之理论篇:Dart语法05(单线程模型,事件循环模型,Isolate)
Flutter开发指南之理论篇:Flutter基础01(架构,设计思想)


 Dart是一门面向对象语言,它针对web 和移动设备开发进行了优化,主要特点为:

  • 一切皆对象!无论是数字,函数还是null,所有对象继承自Object类;
  • 声明一个变量时可以不指定具体类型,Dart可以自动推断类型;
  • Dart支持顶层函数,函数是一等对象,且函数可作为参数传递;
  • Dart使用_开头表示私有属性,没有关键字publicprotectedprivate

1. 单线程模型

众所周知,在Java中使用多线程来处理并发任务,适量并合适地使用多线程,能够极大地提高资源的利用率和程序运行效率,但是缺点也比较明显,比如过度开启线程会带来额外的资源和性能消耗多线程共享内存容易出现死锁等。实际上,在APP的使用过程中,多数处理空闲状态,并不需要进行密集或高并发的处理,因此从某些意义上来说,多线程显得有点多余。正是因为如此,Dart作为一种新的语言,通过引单线程模型很好地处理了并发任务对多线程的依赖。

1.1 单线程模型

Dart是一种单线程语言,因此Dart程序没有主线程和子线程之分,而在Dart中线程并不是指Thread,而是指Isolate。因为Dart没有线程的概念,只有Isolate,每个Isolate都是隔离的,并不会共享内存。所有的Dart代码都是在Isolate中运行,它就像机器上的一个小空间,具有自己的私有内存块和一个运行着事件循环模型的单线程。也就是说,一旦某个Dart函数开始执行,它将执行到这个函数的结束而不被其他Dart代码打断,这就是单线程的特性。

Flutter开发指南之理论篇:Dart语法05(单线程模型,事件循环模型,Isolate)

 默认情况下,Dart程序只有一个Isolate(未自己创建的情况下),而这个Isolate就是Main Isolate。也就是说,一个Dart程序是从Main Isolate的main函数开始的,而在main函数结束后,Main isolate线程开始一个一个处理事件循环模型队列中的每一事件(Event)。上图描述的就是Main Isolate的消息循环模型。

1.2 事件循环模型

也许你会问,既然Dart是一种单线程语言,那么是不是就意味着Dart无法并发处理异步任务了?此言差矣。前面说到,所有的Dart程序都在Isolate中运行,每个Isolate拥有自己的私有内存块和一个事件循环模型,其中,事件循环模型就是用来处理各种事件,比如点输入/输出,点击,定时器以及异步任务等。下图描述了一个Isolate事件循环模型的整个流程:

Flutter开发指南之理论篇:Dart语法05(单线程模型,事件循环模型,Isolate)

 从上图可知,Dart事件循环机制由一个消息循环(event looper)两个消息队列构成,其中,两个消息队列是指事件队列(event queue)微任务队列(Microtask queue)。该机制运行原理为:

  • 首先,Dart程序从main函数开始运行,待main函数执行完毕后,event looper开始工作;
  • 然后,event looper优先遍历执行Microtask队列所有事件,直到Microtask队列为空;
  • 接着,event looper才遍历执行Event队列中的所有事件,直到Event队列为空;
  • 最后,视情况退出循环。

 为了进一步理解,我们解释下上述三个概念:

(1)消息循环(Event Looper)

 顾名思义,消息循环就是指一个永不停歇且不能阻塞的循环,它将不停的尝试从微任务队列事件队列中获取事件(event)进行处理,而这些Event包括了用户输入,点击,Timer,文件IO等。

Flutter开发指南之理论篇:Dart语法05(单线程模型,事件循环模型,Isolate)

(2)事件队列(Event queue)

 该队列的事件来源于外部事件Future,其中,外部事件主要包括I/O,手势,绘制,计时器和isolate相互通信的message等,而Future主要是指用户自定义的异步任务,通过创建Future类实例来向事件队列添加事件。需要注意的是,当Event looper正在处理Microtask Queue时,Event queue会被阻塞,此时APP将无法进行UI绘制,响应用户输入和I/O等事件。下列示例演示了向Event queue中添加一个异步任务事件:

main(List<String> args) {
  print('main start...')
  
  var futureInstance = Future<String>(() => "12345");
  futureInstance.then((res) {
    print(res);
  }).catchError((err) {
    print(err);
  });
  
  print('main end...')
}

// 打印结果:
//      main start...
//      main end...
//      12345

(3)微任务队列(Microtask queue)

该队列的事件来源与当前isolate的内部或通过scheduleMicrotask函数创建,Microtask一般用于非常短的内部异步动作,并且任务量非常少,如果微任务非常多,就会造成Event queue排不上队,会阻塞Event queue的执行造成应用ANR,因为Microtask queue的优先级高于Event queue。因此,大多数情况下的任务优先考虑使用Event queue,不到万不得已不要使用Microtask queue。下列示例演示了两个事件队列执行情况:

import 'dart:async';
main() {
  print('main #1 of 2');
  scheduleMicrotask(() => print('microtask #1 of 2'));

  new Future.delayed(new Duration(seconds:1),
                     () => print('future #1 (delayed)'));
  new Future(() => print('future #2 of 3'));
  new Future(() => print('future #3 of 3'));

  scheduleMicrotask(() => print('microtask #2 of 2'));

  print('main #2 of 2');
}

// 执行结果:
//      main #1 of 2
//      main #2 of 2
//      microtask #1 of 2
//      microtask #2 of 2
//      future #2 of 3
//      future #3 of 3
//      future #1 (delayed)

2. Isolate

大多数计算机中,甚至在移动平台上,都在使用多核CPU。 为了有效利用多核性能,开发者一般使用共享内存数据来保证多线程的正确执行。 然而多线程共享数据通常会导致很多潜在的问题,并导致代码运行出错。Dart作为一种新语言,为了缓解上述问题,提出了Isolate(隔离区)的概念,即Dart没有线程的概念,只有Isolate,所有的Dart代码都是在Isolate中运行,它就像是机器上的一个小空间,具有自己的私有内存堆和一个运行着Event Looper的单个线程。

Flutter开发指南之理论篇:Dart语法05(单线程模型,事件循环模型,Isolate)

 通常,一个Dart应用对应着一个Main Isolate,且应用的入口即为该Isolate的main函数。当然,我们也可以创建其它的Isolate,由于Isolate的内存堆是私有的,因此这些Isolate的内存都不会被其它Isolate访问。假如不同的Isolate需要通信(单向/双向),就只能通过向对方的事件循环队列里写入任务,并且它们之间的通讯方式是通过port(端口)实现的,其中,Port又分为receivePort(接收端口)sendPort(发送端口),它们是成对出现的。Isolate之间通信过程:

  • 首先,当前Isolate创建一个ReceivePort对象,并获得对应的SendPort对象;

    var receivePort = ReceivePort(); var sendPort = receivePort.sendPort;

  • 其次,创建一个新的Isolate,并实现新Isolate要执行的异步任务,同时,将当前Isolate的SendPort对象传递给新的Isolate,以便新Isolate使用这个SendPort对象向原来的Isolate发送事件;

    // 调用Isolate.spawn创建一个新的Isolate // 这是一个异步操作,因此使用await等待执行完毕 var anotherIsolate = await Isolate.spawn(otherIsolateInit, receivePort.sendPort);

    // 新Isolate要执行的异步任务 // 即调用当前Isolate的sendPort向其receivePort发送消息 void otherIsolateInit(SendPort sendPort) async { value = "Other Thread!"; sendPort.send("BB"); }

  • 第三,调用当前Isolate#receivePort的listen方法监听新的Isolate传递过来的数据。Isolate之间什么数据类型都可以传递,不必做任何标记。

    receivePort.listen((date) { print("Isolate 1 接受消息:data = $date"); });

  • 最后,消息传递完毕,关闭新创建的Isolate。

    anotherIsolate?.kill(priority: Isolate.immediate); anotherIsolate =null;

示例代码如下(Isolate单向通信):

import 'dart:isolate';

var anotherIsolate;
var value = "Now Thread!";

void startOtherIsolate() async {
  var receivePort = ReceivePort();

  anotherIsolate = await Isolate.spawn(otherIsolateInit, receivePort.sendPort);

  receivePort.listen((date) {
    print("Isolate 1 接受消息:data = $date,value = $value");
  });
}

void otherIsolateInit(SendPort sendPort) async {
  value = "Other Thread!";
  sendPort.send("BB");
}

// 在Main Isolate创建一个新的Isolate
// 并使用Main Isolate的ReceiverPort接收新Isolate传递过来的数据
import 'DartLib.dart';

void main(){
  startOtherIsolate();
}

 执行结果:

Isolate 1 接受消息:data = BB,value = Now Thread! 

3. 参考文献

1. Dart asynchronous programming: Isolates and event loops
2. Futures - Isolates - Event Loop
3. Flutter 真异步

本文同步分享在 博客“phyger”(CNBlog)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
2年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Wesley13 Wesley13
2年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这