美文网首页
Dart 异步(一)线程

Dart 异步(一)线程

作者: 陆元伟 | 来源:发表于2023-01-08 17:43 被阅读0次

    单线程事件队列

    Dart程序是单线程+事件驱动方式运行,Dart代码的运行就是在不停的在处理一个又一个的事件
    而 Dart 的单线程跟 Java 的完全不同,虽然都是单线程,但是 Dart 分为三个形式:

    主线程
    微任务队列
    事件任务队列
    

    主线程

    跟 Java 一样,具有唯一性,也就是从main()开始的线程。

    微任务队列

    表示一个短时间内就会完成的异步任务,它的优先级比事件队列高。
    主要是通过scheduleMicrotask进行调度。

    事件任务队列

    包含所有的外来事件,比如:I/O、手势、绘图等。,例如:I/O 事件任务、Timer 事件任务等。

    Flutter 任务队列的执行图:


    图片.png

    优先级

    1 主线程 > 微任务队列 > 事件任务队列。

    所以,在 Dart 单线程中,会优先执行完主线程,在执行主线程的过程中,若发现微任务或者事件任务,就会将他们放入相应的任务队列中,然后就会一个个执行完微任务队列里面的微任务,其次再去执行事件任务队列里面的事件任务。

    示例代码如下

         Timer.run(() {
           print('Timer run');
         });
         scheduleMicrotask(() {
           print('scheduleMicrotask run');
         });
          print('main run');
    

    打印输出,可以看出,即使我们把事件任务队列放在最前面,它也是最后执行

    main run
    scheduleMicrotask run
    Timer run
    
    Process finished with exit code 0
    

    再来个复杂例子,在事件任务里面添加微任务,在微任务里面添加事件任务

     //主线程
        print('main start!');
        //事件任务
        Timer.run(() {
          print('事件任务 start!');
          //子微任务
          scheduleMicrotask(() {
            print('子微任务执行!');
          });
          print('事件任务 end!');
        });
        //微任务
        scheduleMicrotask(() {
          print('微任务 start!');
          //子事件任务
          Timer.run(() {
            print('微任务中子事件任务执行!');
          });
          scheduleMicrotask((){
            print('微任务微子事件任务执行!');
          });
          print('微任务 end!');
        });
        print('main end!');
    

    输出

    main start!
    main end!
    微任务 start!
    微任务 end!
    微任务微子事件任务执行!
    事件任务 start!
    事件任务 end!
    子微任务执行!
    微任务中子事件任务执行!
    

    首先是main任务执行完,然后再执行微任务,等微任务完成后,再执行事件任务,等事件任务执行完,再微任务,事件任务。

    添加sleep延迟

      //主线程
          print('main start!');
          sleep(Duration(seconds: 2));
          //微任务
          scheduleMicrotask((){
            print('微任务 start!');
            sleep(Duration(seconds: 2));
            //子事件任务
            Timer.run(() {
              print('子事件任务执行!');
              sleep(Duration(seconds: 2));
            });
            print('微任务 end!');
          });
          //事件任务
          Timer.run(() {
            print('事件任务 start!');
            sleep(Duration(seconds: 2));
            //子微任务
            scheduleMicrotask((){
              print('子微任务执行!');
              sleep(Duration(seconds: 2));
            });
            print('事件任务 end!');
          });
          print('main end!');
    

    输出如下,由于dart本来就是单线程,,隐藏sleep后,后面的任务都暂停了,因此任务顺序不变

    main start!
    main end!
    微任务 start!
    微任务 end!
    事件任务 start!
    事件任务 end!
    子微任务执行!
    子事件任务执行!
    

    结论: 在 Dart 单线程中,主线程、微任务队列、事件任务队列是线性执行的,后面的队列必须等到前面队列都运行完后再执行

    多线程

    在一个页面中做耗时比较大的运算时,就算用了 async、await 异步处理,UI页面的动画还是会卡顿,因为还是在这个UI线程中做运算,异步只是你可以先做其他,等我这边有结果再返回,但是,我们的计算仍旧是在这个UI线程,仍会阻塞UI的刷新,异步只是在同一个线程的并发操作。所以这个时候就需要创建新的线程来执行耗时操作解决这个问题。

    什么是 Isolate

    Isolate 是 Dart 平台对线程的实现方案,但和普通 Thread 不同的是,isolate 拥有独立的内存,isolate 由线程和独立内存构成。正是由于 isolate 线程之间的内存不共享,所以 isolate 线程之间并不存在资源抢夺的问题,所以也不需要锁。通过 isolate 可以很好的利用多核 CPU,来进行大量耗时任务的处理。

    print('main start');
        Isolate.spawn((msg){
          print('spawn 1');
          print(msg);
        },'1');
        Isolate.spawn((msg){
          print('spawn 2');
          print(msg);
        },'2');
        Isolate.spawn((msg){
          print('spawn 3');
          print(msg);
        },'3');
        print('main end');
    

    输出如下(每次输出不一样)

    main start
    spawn 1
    1
    main end
    spawn 3
    3
    spawn 2
    2
    

    Isolate 通信机制

    isolate 之间的通信主要通过 SendPort和ReceivePort来进行。主要流程是:先初始化一个ReceivePort,然后调用listen就可以监听其他isolate发送的消息。
    那其他isolate如何给当前isolate发送消息?答案就是获取到ReceivePort的sendPort。

    ReceivePort:接收其他Isolate数据
    SendPort:发送给其他Isolate数据

    代码如下,由于生成Isolate的spawn方法第二个参数就是传递给Isolate的数据,因此我们把主isolate的SendPort发送过去,这样子isolate获取到主Isolate的SendPort,就可以调用sendPort的send方法给主Isolate发送数据,主Isolate就可以接收到。

    那主Isolate除了创建时给子Isolate发送数据,后续如何给子Isolate继续发送数据呢?

    这就要求子Isolate把它自己的SendPort发送给主Isolate

        print('main start');
        ReceivePort port = ReceivePort();
         Isolate iso = await Isolate.spawn<SendPort>((message) {
          print('子iso:发送消息gaga');
          message.send('gaga');
          print('子iso:发送成功');
    
          ReceivePort iosPort = ReceivePort();
          message.send(iosPort.sendPort);
          iosPort.listen((msg) {
            print('子iso:$msg');
          });
          print('子iso:发送port成功');
        }, port.sendPort);
    
        port.listen((message) {
          print('主iso:收到子iso $message');
          if(message is SendPort){
            message.send('主线程发送子线程消息');
            port.close();
          }
        });
     print('main end');
    

    输出如下

    main start
    main end
    子iso:发送消息gaga
    子iso:发送成功
    子iso:发送port成功
    主iso:收到子iso gaga
    主iso:收到子iso SendPort
    子iso:主线程发送子线程消息
    

    参考文章:
    Dart单线程理解(与Java完全不同)
    Flutter异步编程-Isolate
    深入理解Flutter/Dart事件机制

    相关文章

      网友评论

          本文标题:Dart 异步(一)线程

          本文链接:https://www.haomeiwen.com/subject/yvokqdtx.html