前言
在Flutter中,启动一个新线程来处理任务通常是指在另一个隔离区(isolate)中执行代码。由于Dart使用单线程模型,它通过隔离区来实现并发。隔离区是独立的执行线程,不共享内存,通过消息传递来通信。这种方法可以用来运行长时间或资源密集型的任务,而不会阻塞主UI线程。
为什么Dart是单线程模型
Dart 的单线程模型避免了诸如线程同步、锁定、死锁和竞争条件等复杂问题,避免了线程间的资源冲突。
Dart 使用事件循环来管理事件和消息。这种机制类似于JavaScript的处理方式,允许执行环境管理大量的异步操作。Dart 的事件循环使得它在处理I/O密集型操作(如网络通信、文件操作等)时非常高效,因为它可以在等待这些操作完成时继续执行其他代码,从而不阻塞主线程。
虽然Dart是基于单线程模型,但它通过隔离区(独立的工作区域)支持并发执行。每个隔离区有自己的内存堆和事件循环,它们通过消息传递相互通信,这避免了传统多线程编程中常见的内存共享问题。这种模型在提供并发性的同时,还保持了代码的安全性和可维护性。
隔离区的并发不依赖于操作系统的进程调度,而是由Dart虚拟机(VM)控制。Dart VM 管理所有隔离区,并在可用的处理器核心之间调度它们的执行。
如何在flutter新建一个“线程”
定义一个函数,这个函数将在新的隔离区中运行。这个函数可以执行任何长时间运行的任务,比如数据库操作、文件处理等。
void doWork(SendPort sendPort) {// 执行一些长时间的任务sendPort.send("任务完成");
}
然后可以在Flutter应用中的任何位置启动一个新的隔离区,并传递上面创建的函数给它,同时还需要创建一个ReceivePort
来接收来自隔离区的消息。
void startIsolate() async {// 创建一个ReceivePort用于接收来自隔离区的消息ReceivePort receivePort = ReceivePort();// 启动一个隔离区,并传递给它一个SendPortIsolate.spawn(doWork, receivePort.sendPort);// 监听来自隔离区的消息receivePort.listen((data) {print(data); // 输出从隔离区接收到的数据});
}
Future
在Dart中,Future
是一个核心类,用于表示一个可能在未来某一时刻返回结果的异步操作。当执行一个耗时操作,如网络请求或文件读取时,这个操作将不会立即完成,并且其结果将在未来某个时刻可用。Future
就是用来处理这种情况的。
Future<String> fetchData() {return Future.delayed(Duration(seconds: 4), () {return 'Data loaded';});
}
async和await
这两个关键字一起使用,简化了异步编程的复杂性,使得异步代码的书写和阅读更接近同步代码的风格。
-
async: 用于声明一个函数是异步的。它修改了函数的行为,使得函数返回一个
Future
。这意味着函数即使完成执行,其返回的结果也可能还不可用。 -
await: 用在异步函数(被
async
修饰的函数)内部,用来暂停执行当前的异步函数,直到某个异步操作完成并返回结果。await
只能在async
函数中使用。
将上面的fetchData
函数与await
一起使用,看看如何在实际代码中调用它:
Future<void> printData() async {String data = await fetchData(); // 等待fetchData函数的Future完成print(data); // 打印加载的数据
}
在printData
函数中,使用await
关键字暂停执行,直到fetchData
的Future
完成。这种方式使得异步代码的流程看起来像是同步的,便于理解和维护。
在Dart中,当使用async
和await
时,实际上是在当前的执行线程(通常是主线程)上调度异步任务。这些任务被安排在事件队列中,等待事件循环来处理。当一个异步操作(如网络请求、文件I/O等)被触发时,它会被排入事件循环,并在操作完成后的某个时刻返回结果,此时await
会恢复函数的执行。
在Dart中,这种看似同时进行的操作实际上是通过事件循环(event loop)来实现的,这是一种典型的异步编程模式。
Dart 的单线程模型并不意味着它不能同时处理多个任务。实际上,Dart 使用一个叫做事件循环的系统来高效地管理单线程中的多个任务。这里的关键是任务被分为微任务(microtasks)和事件(events):
微任务(Microtasks):这些任务拥有高优先级,通常用于处理短暂且紧急的工作。微任务队列在事件队列之前执行,并且一旦开始,整个微任务队列会被执行完毕,直到没有微任务为止。
事件(Events):这些任务通常包括I/O操作,如网络请求、文件操作、定时器等。事件队列中的任务会在微任务队列为空时执行。