在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
库导入标准库 import 'dart:math'; 也可以用相对路径或绝对路径来引用dart文件 import 'lib/student/student.dart'; 指定package:前缀,表示导入包管理系统中的库 import 'package:utils/utils.dart';
导入库时,可以使用 import 'package:lib1/lib1.dart'; import 'package:lib2/lib2.dart' as lib2; // 使用lib1中的Element Element element1 = new Element(); // 使用lib2中的Element lib2.Element element2 = new lib2.Element(); 使用 // 仅导入foo,屏蔽库中其他成员 import 'package:lib1/lib1.dart' show foo; // 屏蔽foo,库中其他成员都可见 import 'package:lib2/lib2.dart' hide foo; 为了减少 APP 的启动时间,加载很少使用的功能,我们还可以延迟导入库。使用 import 'package:deferred/hello.dart' deferred as hello; // 当需要使用时,再通过库标识符调用 loadLibrary函数加载 hello.loadLibrary(); 异步编程
Dart是单线程模型,也就没有了所谓的主线程/子线程之分。 1、异步: 多线程虽然好用,但是在大量并发时,仍然存在两个较大的缺陷,一个是开辟线程比较耗费资源,线程开多了机器吃不消,另一个则是线程的锁问题,多个线程操作共享内存时需要加锁,复杂情况下的锁竞争不仅会降低性能,还可能造成死锁。因此又出现了基于事件的异步模型。 我们很容易发现,这种基于事件的异步模型,只适合I/O密集型的耗时操作,因为I/O耗时操作,往往是把时间浪费在等待对方传送数据或者返回结果,因此这种异步模型往往用于网络服务器并发。如果是计算密集型的操作,则应当尽可能利用处理器的多核,实现并行计算。
2、Dart 的事件循环 Dart 是事件驱动的体系结构,该结构基于具有单个事件循环和两个队列的单线程执行模型。 Dart虽然提供调用堆栈。 但是它使用事件在生产者和消费者之间传输上下文。 事件循环由单个线程支持,因此根本不需要同步和锁定。 Dart 的两个队列分别是
Dart中的Main Isolate只有一个Event Looper,但是存在两个Event Queue:Event Queue以及Microtask Queue
Dart事件循环执行如上图所示
注意:我们可以看出,将任务加入到
Event-Queue Dart也是Event-Looper以及Event-Queue的模型,所有的事件都是通过EventLooper的依次执行。 而Dart的Event Loop就是:
而这些Event包括了用户输入,点击,Timer,文件IO等
3、调度任务 a、任务添加到
import 'dart:async'; //我的任务队列 void myTask(){ print("this is my task"); } void main() { # 1. 使用 scheduleMicrotask 方法添加 scheduleMicrotask(myTask); # 2. 使用Future对象添加 new Future.microtask(myTask); }
b、将任务添加到 import 'dart:async'; //我的任务 void myTask(){ print("this is my task"); } void main() { # 1. 使用Future对象添加 new Future(myTask); }
c、编写代码验证以上的结论 import 'dart:async' void main() { print('main Start'); new Future((){ print('this is my task'); }); new Future.microtask((){ print('this is microtask'); }); print('main Stop'); } 打印结果: main start main stop this is microtask this is my task
4、延时任务 Future.delayed
import 'dart:async'; import 'dart:io'; void main() { print("main start"); new Future.delayed(new Duration(seconds:1),(){ print('task delayed'); }); new Future((){ // 模拟耗时5秒 sleep(Duration(seconds:5)); print("5s task"); }); print("main stop"); } 打印结果: main start
main stop
5s task
task delayed
5、Future详解 Future类是对未来结果的一个代理,它返回的并不是被调用的任务的返回值。 //我的任务 void myTask(){ print("this is my task"); } void main() { Future fut = new Future(myTask);//根据我的任务创建Future对象 } 如上代码,
注册回调 import 'dart:async'; void main() { print("main start"); Future fut =new Future.value(18); // 使用then注册回调 fut.then((res){ print(res); }); // 链式调用,可以跟多个then,注册多个回调 new Future((){ print("async task"); }).then((res){ print("async task complete"); }).then((res){ print("async task after"); }); print("main stop"); }
除了 new Future((){ print("async task"); }).then((res){ print("async task complete"); }).catchError((e){ print(e); });
还可以使用静态方法 import 'dart:async'; void main() { print("main start"); Future task1 = new Future((){ print("task 1"); return 1; }); Future task2 = new Future((){ print("task 2"); return 2; }); Future task3 = new Future((){ print("task 3"); return 3; }); Future fut = Future.wait([task1, task2, task3]); fut.then((responses){ print(responses); }); print("main stop"); }
6、async 和 await // 导入io库,调用sleep函数 import 'dart:io'; // 模拟耗时操作,调用sleep函数睡眠2秒 doTask() async{ await sleep(const Duration(seconds:2)); return "Ok"; } // 定义一个函数用于包装 test() async { var r = await doTask(); print(r); } void main(){ print("main start"); test(); print("main end"); } async 不是并行执行,它是遵循Dart 事件循环规则来执行的,它仅仅是一个语法糖,简化Future API的使用。
总结
7、Isolate(隔离) 协程 前面已经说过,将非常耗时的任务添加到事件队列后,仍然会拖慢整个事件循环的处理,甚至是阻塞。可见基于事件循环的异步模型仍然是有很大缺点的,这时候我们就需要Isolate,这个单词的中文意思是隔离。 简单说,可以把它理解为Dart中的线程。但它又不同于线程,更恰当的说应该是微线程,或者说是协程。它与线程最大的区别就是不能共享内存,因此也不存在锁竞争问题,两个Isolate完全是两条独立的执行线,且每个Isolate都有自己的事件循环,它们之间只能通过发送消息通信,所以它的资源开销低于线程。 isolate 本身是隔离的意思,有自己的内存和单线程控制的实体,因为isolate 之间的内存在逻辑是隔离的,isolate 的代码是按顺序执行的。在Dart 中并发可以使用用isolate ,isolate 和Thread 很像,但是isolate 之间没有共享内存。一个Dart 程序是在Main isolate 的Main函数开始,我们平时开发中,默认环境就是Main isolate ,App的启动入口main 函数就是一个isolate ,在Main函数结束后,Main isolate 线程开始一个一个处理Event Queue 中的每一个Event 。
整个消息通信过程: 两个Isolate是通过两对Port对象通信,一对Port分别由用于接收消息的ReceivePort对象,和用于发送消息的SendPort对象构成。 其中SendPort对象不用单独创建,它已经包含在ReceivePort对象之中。 需要注意,一对Port对象只能单向发消息,这就如同一根自来水管,ReceivePort和SendPort分别位于水管的两头,水流只能从SendPort这头流向ReceivePort这头。因此,两个Isolate之间的消息通信肯定是需要两根这样的水管的,这就需要两对Port对象。 Dart代码中,具体是如何操作的呢?
代码演示:spawnUri和spawn(更常用) a、spawnUri用法 spawnUri方法有三个必须的参数, 第一个是Uri,指定一个新Isolate代码文件的路径, 主 import 'dart:isolate'; void main() { print("main isolate start"); create_isolate(); print("main isolate stop"); } // 创建一个新的 isolate create_isolate() async{ ReceivePort rp = new ReceivePort(); SendPort port1 = rp.sendPort; Isolate newIsolate = await Isolate.spawnUri(new Uri(path: "./other_task.dart"), ["hello, isolate", "this is args"], port1); SendPort port2; rp.listen((message){ print("main isolate message: $message"); if (message[0] == 0){ port2 = message[1]; }else{ port2?.send([1,"这条信息是 main isolate 发送的"]); } }); // 可以在适当的时候,调用以下方法杀死创建的 isolate // newIsolate.kill(priority: Isolate.immediate); } 创建 import 'dart:isolate'; import 'dart:io'; void main(args, SendPort port1) { print("isolate_1 start"); print("isolate_1 args: $args"); ReceivePort receivePort = new ReceivePort(); SendPort port2 = receivePort.sendPort; receivePort.listen((message){ print("isolate_1 message: $message"); }); // 将当前 isolate 中创建的SendPort发送到主 isolate中用于通信 port1.send([0, port2]); // 模拟耗时5秒 sleep(Duration(seconds:5)); port1.send([1, "isolate_1 任务完成"]); print("isolate_1 stop"); } 运行主Isolate结果 main isolate start main isolate stop isolate_1 start isolate_1 args: [hello, isolate, this is args] main isolate message: [0, SendPort] isolate_1 stop main isolate message: [1, isolate_1 任务完成] isolate_1 message: [1, 这条信息是 main isolate 发送的]
b、spawn用法 spawn更常用: 我们通常希望将新创建的Isolate代码和main Isolate代码写在同一个文件,且不希望出现两个main函数 而是将指定的耗时函数运行在新的Isolate,这样做有利于代码的组织和代码的复用 spawn方法有两个必须的参数,第一个是需要运行在新Isolate的耗时函数,第二个是动态消息,该参数通常用于传送主Isolate的SendPort对象。 spawn的用法: import 'dart:isolate'; import 'dart:io'; void main() { print("main isolate start"); create_isolate(); print("main isolate end"); } // 创建一个新的 isolate create_isolate() async{ ReceivePort rp = new ReceivePort(); SendPort port1 = rp.sendPort; Isolate newIsolate = await Isolate.spawn(doWork, port1); SendPort port2; rp.listen((message){ print("main isolate message: $message"); if (message[0] == 0){ port2 = message[1]; }else{ port2?.send([1,"这条信息是 main isolate 发送的"]); } }); } // 处理耗时任务 void doWork(SendPort port1){ print("new isolate start"); ReceivePort rp2 = new ReceivePort(); SendPort port2 = rp2.sendPort; rp2.listen((message){ print("doWork message: $message"); }); // 将新isolate中创建的SendPort发送到主isolate中用于通信 port1.send([0, port2]); // 模拟耗时5秒 sleep(Duration(seconds:5)); port1.send([1, "doWork 任务完成"]); print("new isolate end"); } 运行结果 main isolate start main isolate end new isolate start main isolate message: [0, SendPort] new isolate end main isolate message: [1, doWork 任务完成] doWork message: [1, 这条信息是 main isolate 发送的]
无论是上面的spawn还是spawnUri,运行后都会创建Isolate,一个是主Isolate,一个是新Isolate,两个都双向绑定了消息通信的通道,即使新的Isolate中的任务完成了,它也不会立刻退出,因此,当使用完自己创建的Isolate后,最好调用newIsolate.kill(priority: Isolate.immediate);将Isolate立即杀死。 8、Flutter中创建Isolate
在Dart中创建一个Isolate都显得有些繁琐,Dart官方并未提供更高级封装
但是,如果想在Flutter中创建Isolate,则有更简便的API
compute函数
import 'package:flutter/foundation.dart'; import 'dart:io'; // 创建一个新的Isolate,在其中运行任务doWork create_new_task() async{ var str = "New Task"; var result = await compute(doWork, str); print(result); } void doWork(String value){ print("new isolate doWork start"); // 模拟耗时5秒 sleep(Duration(seconds:5)); print("new isolate doWork end"); return "complete:$value"; } compute函数有两个必须的参数, 9、使用场景
Isolate虽好,但也有合适的使用场景,不建议滥用Isolate,应尽可能多的使用Dart中的事件循环机制去处理异步任务,这样才能更好的发挥Dart语言的优势。 那么应该在什么时候使用Future,什么时候使用Isolate呢? 方法执行在几毫秒或十几毫秒左右的,应使用Future,如果一个任务需要几百毫秒或之上的,则建议创建单独的Isolate JSON 解码 加密 图像处理:比如剪裁 网络请求:加载资源、图片 补充进程、线程一道面试题:说说进程和线程的区别
|
请发表评论