美文网首页flutter
flutter-isolate详解

flutter-isolate详解

作者: 浮华_du | 来源:发表于2021-06-25 14:08 被阅读0次

    一. isolate简介

    Dart 是单线程,Dart 为我们提供了 isolate,isolate 跟线程差不多,它可以理解为 Dart 中的线程。isolate 与线程的区别就是线程与线程之间是共享内存的,而 isolate 和 isolate 之间是内存不共享的,所以叫 isolate (隔离)。因此也不存在锁竞争问题,两个Isolate完全是两条独立的执行线,且每个Isolate都有自己的事件循环,它们之间只能通过发送消息通信,所以它的资源开销低于线程。

    原文链接:https://blog.csdn.net/u011578734/article/details/108853613
    大多数计算机中,甚至在移动平台上,都在使用多核 CPU。为了有效利用多核性能,开发者一般使用共享内存的方式让线程并发地运行。然而,多线程共享数据通常会导致很多潜在的问题,并导致代码运行出错。
    为了解决多线程带来的并发问题,Dart 使用 isolates 替代线程,所有的 Dart 代码均运行在一个 isolates 中。每一个 isolates 有它自己的堆内存以确保其状态不被其它 isolates 访问。
    每个 isolate 都拥有自己的事件循环及队列(MicroTask 和 Event)。这意味着在一个 isolate 中运行的代码与另外一个 isolate 不存在任何关联。
    isolate是Dart对actor并发模式的实现。运行中的Dart程序由一个或多个actor组成,这些actor也就是Dart概念里面的isolate。isolate是有自己的内存和单线程控制的运行实体。isolate本身的意思是“隔离”,因为isolate之间的内存在逻辑上是隔离的。isolate中的代码是按顺序执行的,任何Dart程序的并发都是运行多个isolate的结果。因为Dart没有共享内存的并发,没有竞争的可能性所以不需要锁,也就不用担心死锁的问题。

    二. isolate与async关系:

    之前介绍过 async/await Future的原理, async关键字实现了异步操作, 而其所谓的异步其实也是运行在同一线程中并没有开启新的线程, 只是通过单线程的任务调度实现一个先执行其他的代码片段,等这边有结果后再返回的异步效果.

    • Isolate可以实现异步并行多个任务
    • Future实现异步串行多个任务

    举个栗子:

    假设有一个任务,需要计算1+2+...100的和
    我们通常会这么写:

    
    class IsolateTestPage extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return IsolateTestPageState();
      }
    }
    
    class IsolateTestPageState extends State<IsolateTestPage> {
      var content = "点击计算按钮,开始计算";
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Isolate"),
          ),
          body: SafeArea(
            child: Center(
              child: Column(
                children: <Widget>[
                  Container(
                      width: double.infinity, height: 500, child: Text(content)),
                  TextButton(
                    child: Text('计算'),
                    onPressed: () {
                      int result = sum(100);
                      content = "总和$result";
                      setState(() {});
                    },
                  )
                ],
              ),
            ),
          ),
        );
      }
    //计算0到 num 数值的总和
      int sum(int num) {
        int count = 0;
        while (num > 0) {
          count = count + num;
          num--;
        }
        return count;
      }
    }
    

    试一下,结果是可以的;但是如果我们改为计算1+2+...100000000000的和,运行代码会发现,直接卡死了
    这时候就需要创建一个isolate来执行这个耗时的任务,而不影响其他任务的执行. 那么首先,如何创建一个isolate?

    三.创建isolate 及 isolate通信

    • (1).获取当前main isolate的ReceivePort及SendPort
    • (2).使用Isolate.spawn创建新的isolate,需要传入新的isolate需要完成的任务名称及创建者(main isolate)的sendPort. (用于将新的isolate的sendPort传递给创建者)
    • (3).在任务方法中,获取新的isolate的ReceivePort及SendPort
    • (4).将新的isolate的SendPort,通过main isolate的sendPort,发送给main isolate,使新的isolate的SendPort能在main isolate中发送消息
    • (5). SendPort发送消息, ReceivePort接收消息,互相通信

    1. receivePort.listen((message) {}) 能收到其对应的sendPort发送的消息

    _testIsolate() async {
        ReceivePort rp1 = new ReceivePort();
        SendPort port1 = rp1.sendPort;
        // 通过spawn新建一个isolate,并绑定静态方法
        Isolate newIsolate = await Isolate.spawn(doWork, port1);
    
        SendPort port2;
        rp1.listen((message) {
          print("rp1 收到消息: $message"); //2.  4.  7.rp1收到消息
          if (message[0] == 0) {
            port2 = message[1]; //得到rp2的发送器port2
          } else {
            if (port2 != null) {
              print("port2 发送消息");
              port2?.send([1, "这条信息是 port2 在main isolate中 发送的"]); // 8.port2发送消息
            }
          }
        });
    
        print("port1--main isolate发送消息");
        port1.send([1, "这条信息是 port1 在main isolate中 发送的"]); //1.port1发送消息
    
        // newIsolate.kill();
      }
    
    // 新的isolate中可以处理耗时任务
      static void doWork(SendPort port1) {
        ReceivePort rp2 = new ReceivePort();
        SendPort port2 = rp2.sendPort;
        rp2.listen((message) {
          //9.10 rp2收到消息
          print("rp2 收到消息: $message");
        });
        // 将新isolate中创建的SendPort发送到main isolate中用于通信
        print("port1--new isolate发送消息");
        port1.send([0, port2]); //3.port1发送消息,传递[0,rp2的发送器]
        // 模拟耗时5秒
        sleep(Duration(seconds: 5));
        print("port1--new isolate发送消息");
        port1.send([1, "这条信息是 port1 在new isolate中 发送的"]); //5.port1发送消息
        print("port2--new isolate发送消息");
        port2.send([1, "这条信息是 port2 在new isolate中 发送的"]); //6.port2发送消息
      }
    
    //I/flutter (14639): port1--main isolate发送消息
    //I/flutter (14639): rp1 收到消息: [1, 这条信息是 port1 在main isolate中 发送的]
    //I/flutter (14639): port1--new isolate发送消息
    //I/flutter (14639): rp1 收到消息: [0, SendPort]
    //I/flutter (14639): port1--new isolate发送消息
    //I/flutter (14639): port2--new isolate发送消息
    //I/flutter (14639): rp1 收到消息: [1, 这条信息是 port1 在new isolate中 发送的]
    //I/flutter (14639): port2 发送消息
    //I/flutter (14639): rp2 收到消息: [1, 这条信息是 port2 在new isolate中 发送的]
    //I/flutter (14639): rp2 收到消息: [1, 这条信息是 port2 在main isolate中 发送的]
    
    

    2. dynamic result = await receivePort.first; 只能收到第一条消息

     _testIsolate() async {
        ReceivePort rp1 = new ReceivePort();
        SendPort port1 = rp1.sendPort;
        // 通过spawn新建一个isolate,并绑定静态方法
        Isolate newIsolate = await Isolate.spawn(doWork, port1);
    
        SendPort port2;
        dynamic receiveMsg = await rp1.first; //只拿到第一条收到结果
        print('rp1 收到消息--$receiveMsg');
        if (receiveMsg is SendPort) {
          SendPort port2 = receiveMsg;
          // print('rp1 收到消息--port2');
          port2.send([1, "这条信息是 port2 在main isolate中 发送的"]);
        }
    
        // newIsolate.kill();
      }
    
    // 新的isolate中可以处理耗时任务
      static void doWork(SendPort port1) {
        ReceivePort rp2 = new ReceivePort();
        SendPort port2 = rp2.sendPort;
        rp2.listen((message) {
          print("rp2 收到消息-- $message");
        });
        // 将新isolate中创建的SendPort发送到main isolate中用于通信
        print("port1--new isolate发送消息--port2");
        port1.send(port2);
        // 模拟耗时5秒
        sleep(Duration(seconds: 5));
        print("port1--new isolate发送消息--啊哈哈");
        port1.send("啊哈哈");
      }
    

    3.解决示例问题

    知道怎么创建isolate了,我们再看怎么来计算1+2+...100000000000的和

    (1)使用listen监听,结果回调的形式

    1.创建isolate
    2.打通两个isolate的通道(能互相发送消息)
    3.main isolate将要计算的最大数传递给new isolate; newisolate计算,计算完成后,将结果发送回 main isolate

    calculation(int n, Function(int result) success) async {
        //创建一个ReceivePort
        final receivePort1 = new ReceivePort();
        //创建isolate
        Isolate isolate = await Isolate.spawn(createIsolate, receivePort1.sendPort);
        receivePort1.listen((message) {
          if (message is SendPort) {
            SendPort sendPort2 = message;
            sendPort2.send(n);
          } else {
            print(message);
            success(message);
          }
        });
      }
    
      //创建isolate必须要的参数
      static void createIsolate(SendPort sendPort1) {
        final receivePort2 = new ReceivePort();
        //绑定
        print("sendPort1发送消息--sendPort2");
        sendPort1.send(receivePort2.sendPort);
        //监听
        receivePort2.listen((message) {
          //获取数据并解析
          print("receivePort2接收到消息--$message");
          if (message is int) {
            num result = summ(message);
            sendPort1.send(result);
          }
        });
      }
    
      //计算0到 num 数值的总和
      static num summ(int num) {
        int count = 0;
        while (num > 0) {
          count = count + num;
          num--;
        }
        return count;
      }
    

    (2)receivePort.first只能收到第一条消息; 我们可以再创建一个ReceivePort用来传递消息,如下:

    static Future<dynamic> calculation(int n) async {
        //创建一个ReceivePort
        final receivePort1 = new ReceivePort();
        //创建isolate
        Isolate isolate = await Isolate.spawn(createIsolate, receivePort1.sendPort);
    
        //使用 receivePort1.first 获取sendPort1发送来的数据
        final sendPort2 = await receivePort1.first as SendPort;
        print("receivePort1接收到消息--sendPort2");
        //接收消息的ReceivePort
        final answerReceivePort = new ReceivePort();
        print("sendPort2发送消息--[$n,answerSendPort]");
        sendPort2.send([n, answerReceivePort.sendPort]);
        //获得数据并返回
        num result = await answerReceivePort.first;
        print("answerReceivePort接收到消息--计算结果$result");
        return result;
      }
    
      //创建isolate必须要的参数
      static void createIsolate(SendPort sendPort1) {
        final receivePort2 = new ReceivePort();
        //绑定
        print("sendPort1发送消息--sendPort2");
        sendPort1.send(receivePort2.sendPort);
        //监听
        receivePort2.listen((message) {
          //获取数据并解析
          print("receivePort2接收到消息--$message");
          final n = message[0] as num;
          final send = message[1] as SendPort;
          //返回结果
          num result = summ(n);
          print("answerSendPort发送消息--计算结果$result");
          send.send(result);
        });
      }
    
      //计算0到 num 数值的总和
      static num summ(int num) {
        int count = 0;
        while (num > 0) {
          count = count + num;
          num--;
        }
        return count;
      }
    

    4. isolate的暂停 恢复 结束

        //恢复 isolate 的使用
        isolate.resume(isolate.pauseCapability);
    
        //暂停 isolate 的使用
        isolate.pause(isolate.pauseCapability);
    
        //结束 isolate 的使用
        isolate.kill(priority: Isolate.immediate);
    
        //赋值为空 便于内存及时回收
        isolate = null;
    

    四.flutter中创建isolate---compute()方法

    TextButton(
                    child: Text('flutter创建isolate'),
                    onPressed: () async {
                      num result = await compute(summ, 10000000000);
                      content = "计算结果$result";
                      setState(() {});
                    },
                  ),
    

    到这里,希望大家对isolate有更深一步的了解.欢迎点点小心心哦~

    https://cloud.tencent.com/developer/article/1676442

    相关文章

      网友评论

        本文标题:flutter-isolate详解

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