Dart 是不是单线程模型?是如何运行的?
是,异步 IO + 事件循环
I/O 模型
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态
- 阻塞式调用: 调用结果返回之前,当前线程会被挂起,调用线程只有在得到调用结果之后才会继续执行。
- 非阻塞式调用: 调用执行之后,当前线程不会停止执行,只需要过一段时间来检查一下有没有结果返回即可。
事件循环(Event Loop)
Dart事件循环机制由一个消息循环(event looper)和两个消息队列构成,其中,两个消息队列是指事件队列(event queue)和微任务队列(Microtask queue)。该机制运行原理为:
- 首先,Dart程序从main函数开始运行,待main函数执行完毕后,event looper开始工作;
- 然后,event looper优先遍历执行Microtask队列所有事件,直到Microtask队列为空;
- 接着,event looper才遍历执行Event队列中的所有事件,直到Event队列为空;
- 最后,视情况退出循环。
Dart 是如何实现多任务并行的?
尽管 Dart 是基于单线程模型的,但为了进一步利用多核 CPU,Dart 也提供了多线程机制,即 Isolate。
每个 Isolate 都有自己的 Event Loop 与 Queue,Isolate 之间不共享任何资源,只能依靠消息机制通信,因此也就没有资源抢占问题。
receivePort(接收端口)和sendPort(发送端口)
,它们是成对出现的
假如不同的Isolate需要通信(单向/双向),就只能通过向对方的事件循环队列里写入任务,并且它们之间的通讯方式是通过port(端口)实现的,其中,Port又分为receivePort(接收端口)和sendPort(发送端口)
,它们是成对出现的。Isolate之间通信过程:
- 首先,当前Isolate创建一个ReceivePort对象,并获得对应的SendPort对象;
ini
var receivePort = ReceivePort();
var sendPort = receivePort.sendPort;
- 其次,创建一个新的Isolate,并实现新Isolate要执行的异步任务,同时,将当前Isolate的SendPort对象传递给新的Isolate,以便新Isolate使用这个SendPort对象向原来的Isolate发送事件;
csharp
// 调用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之间什么数据类型都可以传递,不必做任何标记
lua
receivePort.listen((date) {
print("Isolate 1 接受消息:data = $date");
});
- 最后,消息传递完毕,关闭新创建的Isolate。
ini
anotherIsolate?.kill(priority: Isolate.immediate);
anotherIsolate =null;
Future与isolate的区别
- future是异步编程,调用本身立即返回,并在稍后的某个时候执行完成时再获得返回结果。在普通代码中可以使用await 等待一个异步调用结束。
- isolate是并发编程,Dartm有并发时的共享状态,所有Dart代码都在isolate中运行,包括最初的main()。每个isolate都有它自己的堆内存,意味着其中所有内存数据,包括全局数据,都仅对该isolate可见,它们之间的通信只能通过传递消息的机制完成,消息则通过端口(port)收发。isolate只是一个概念,具体取决于如何实现,比如在Dart VM中一个isolate可能会是一个线程,在Web中可能会是一个Web Worker
Future
和 Stream
的异同
- 1、
Future
只能接受一次返回值,stream可以接受多次,可以监听事件流 - 2、stream支持暂停和恢复
简单说一下在Flutter里async和await?Stream? Future和Stream 的异同
在Flutter中,异步编程是通过Future、Stream和async/await等机制来处理的。这些机制允许在执行长时间操作(如网络请求、文件读写等)时不阻塞应用程序的主线程,从而保持应用的响应性。
async
和 await
async
:标记一个函数为异步函数。异步函数可以包含await
表达式,等待异步操作的结果。await
:用于暂停异步函数的执行,直到Future
完成并返回结果。
Future
- 定义:表示一个异步操作的结果,这个结果在将来的某个时刻会返回。
- 特点 :
Future
只能完成一次,返回一个单一的结果或错误。 - 用法:常用于网络请求、文件读取等一次性操作。
csharp
Future<String> fetchUsername() async {
await Future.delayed(Duration(seconds: 1));
return 'User123';
}
void main() async {
String username = await fetchUsername();
print(username);
}
Stream
- 定义:表示一系列异步数据的序列,可以是多个值或错误。
- 特点 :
Stream
可以多次发出数据,类似于异步的数据流。 - 用法:常用于需要多次更新的数据,如实时数据流、用户输入事件等。
csharp
Stream<int> numberStream() async* {
for (int i = 1; i <= 5; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
void main() {
numberStream().listen((number) {
print(number);
});
}
Stream的种类
- "Single-subscription" streams 单订阅流:单个订阅流在流的整个生命周期内仅允许有一个listener
- "broadcast" streams 多订阅流:广播流允许任意数量的收听者,且无论是否有收听者,他都能产生事件。所以中途进来的收听者将不会收到之前的消息。
Future
和 Stream
的异同
特性 | Future |
Stream |
---|---|---|
返回值次数 | 一次 | 多次 |
完成状态 | 完成一次后结束 | 可以多次返回数据,直到结束 |
用途 | 一次性异步操作,如网络请求、文件读取 | 多次更新的数据,如实时数据、用户输入事件 |
监听机制 | await 或 then() 处理单一结果 |
listen() 处理数据流 |
错误处理 | catchError() 或 try-catch 捕获 |
onError 捕获错误,使用 try-catch 捕获 |
暂停和恢复 | 不支持 | 支持暂停和恢复(pause 和 resume ) |
函数
Dart是一门面向对象的语言,所以函数也是对象,并且函数的类型是Function
。
函数的参数可以分成两类: 必须参数和可选参数
可选参数可以分为 命名可选参数 和 位置可选参数
dart
命名可选参数: {param1, param2, ...}
位置可选参数: [param1, param2, ...]
必传参赛: {@required int age,String name}
默认值是编译时常量:[String interests,String sex = "男"]
getter setter 重写
Dart
中所有的基础类型、类等都继承 Object
,默认值是 NULL
, 自带 getter
和 setter
,而如果是 final
或者 const
的话,那么它只有一个 getter
方法,Object
都支持 getter、setter 重写:
kotlin
@override
Size get preferredSize {
return Size.fromHeight(kTabHeight + indicatorWeight);
}
级联符号(..)表示什么意思?
Dart 当中的 「..」意思是 「级联操作符」,为了方便配置而使用。「..」和「.」不同的是 调用「..」后返回的相当于是 this,而「.」返回的则是该方法返回的值 。
它允许你在同一个对象上连续调用多个方法或访问多个成员变量,而无需在每次调用后重新指定对象名。这种做法通常被称为"级联表达式"或 "链式调用" 。
ini
someObject
..method1()
..property1 = value1
..method2();
Dart 的作用域
Dart 没有 「public」「private」等关键字,默认就是公开的,私有变量使用 下划线 _开头。
变量 dynamic,var,object三者的区别
- 1、var声明的变量一经赋值后,数据类型就已经确定,不可以接收其他的数据类型
- 2、dynamic声明的变量在第一次赋值后,可以继续接收其他的数据类型,可以使用变量运行时的属性跟方法
- 3、Object声明的变量只能使用Object本身的属性以及方法
final与const声明常量
- 1、const与final 都用于声明常量,而且已经赋值都不可以被修改
- 2、const声明常量时,必须赋值明确的值,final声明的常量可以在运行时再赋值
简述Dart语言特性
- 1、Dart 属于是强类型语言 , 以用var或 dynamic来声明一个变量,Dart会自动推断其数据类型,dynamic类似c#没有赋初值的变量都会有默认值null
- 2、在Dart中,一切都是对象,所有的对象都是继承自Object
Dart是值传递还是引用传递
dart是引用传递
每次调用函数,传递过去的都是对象的内存地址,而不是这个对象的复制。
Dart 中的 extends / with / implements
-
extends 表示继承。Dart 中的继承和 Java 一样。
- 使用关键字 extends 继承一个类
- 子类继承父类可见的属性和方法,不会继承父类的构造方法
- 子类可以覆写父类的方法,方法上要加 @override
- 子类可以调用父类的方法,用 super 关键字
- 单继承,多态性
-
with 表示 mixins (混入),就是在类中混入其他功能。使用 mixins 可以实现类似于多继承的功能。mixins 和接口完全不一样,是一个全新的特性。
- mixins 只能继承 Object
- mixins 没有构造函数
- mixins 和接口实现一样可以在 with 后面使用多个 mixins,用逗号隔开
-
implements 表示接口实现。Dart 不使用 interface 关键字,但是 Dart 中每个类都是一个隐性的接口,这个隐性的接口里面包含了类的所有成员变量和方法。如果一个类被当做接口来用,那么实现这个接口的类必须实现接口所有的成员变量和方法
dart是单继承还是多继承?
单继承
dart如何达到多继承的效果?
Dart中使用Mixins,可以达到多继承的效果
mixin混入有什么特点
- 1、作为mixins的类只能继承自Object,不能继承其他类
- 2、作为mixins的类不能有构造函数
- 3、一个类可以mixins多个mixins类
- 4、mixins绝不是继承,也不是接口,而是一种全新的特性
混入相同方法的多个混入,最终会执行哪一个?
后面的类中的方法将前面的类中相同的方法覆盖
String扩展一个方法
使用关键字extension ... on
为String定义一个扩展类
dart
extension StringExt on String{
// 2. 扩展方法
int add(int x, int y){
return x + y;
}
}