美文网首页
flutter中如何创建并且使用一个常驻isolate

flutter中如何创建并且使用一个常驻isolate

作者: 晨曦中的花豹 | 来源:发表于2022-03-25 16:28 被阅读0次

    上一篇文章说到了ffi调用c函数,但是结尾留了一个问题就是c函数如果是一个延时操作,整个流程会调用多次,并且每一个c函数调用都使用了compute进行异步调用(这里不在业务层处理主要是想要业务层显得简洁,其实可以使用compute对整个业务进行异步),这个时候compute频繁创建isolate导致整个流程下来消耗时间过长,然后就有了一个想法,我能不能创建一个常驻的isolate,去执行我的c函数呢
    我的思路是

    1.熟悉isolate通信

    其实最后看下来简单的理解就是两个(A,B)封闭的空间各干各的,中间有两个管道(Port),一个是从A->B,一个是从B->A


    WechatIMG23.jpeg

    A把要干的事通过管子给到B,B监听到后开始工作,结束之后把结果再通过管道给到A,基本的流程就是这样,撸代码

    //特别需要注意:establishConn执行环境是rootIsolate
      static void establishConn() async {
        //第1步: 默认执行环境下是rootIsolate,所以创建的是一个rootIsolateReceivePort
        ReceivePort rootIsolateReceivePort = ReceivePort();
        //第2步: 获取rootIsolateSendPort
        SendPort rootIsolateSendPort = rootIsolateReceivePort.sendPort;
        //第3步: 创建一个newIsolate实例,并把rootIsolateSendPort作为参数传入到newIsolate中,为的是让newIsolate中持有rootIsolateSendPort, 这样在newIsolate中就能向rootIsolate发送消息了
        newIsolate = await Isolate.spawn(createNewIsolateContext,
            rootIsolateSendPort); //注意createNewIsolateContext这个函数执行环境就会变为newIsolate, rootIsolateSendPort就是createNewIsolateContext回调函数的参数
        //第4步: 通过rootIsolateReceivePort接收到来自newIsolate的消息,所以可以注意到这里是await 因为是异步消息
        rootIsolateReceivePort.listen((message) {
          if (message is SendPort) {
            print("newIsolateSendPort $message");
            newIsolateSendPort = message;
          }
        });
      }
    
      //特别需要注意:createNewIsolateContext执行环境是newIsolate
      static void createNewIsolateContext(SendPort rootIsolateSendPort) async {
        //第1步: 注意callback这个函数执行环境就会变为newIsolate, 所以创建的是一个newIsolateReceivePort
        ReceivePort newIsolateReceivePort = ReceivePort();
        //第2步: 获取newIsolateSendPort, 有人可能疑问这里为啥不是直接让全局newIsolateSendPort赋值,注意这里执行环境不是rootIsolate
        SendPort newIsolateSendPort = newIsolateReceivePort.sendPort;
        //第3步: 特别需要注意这里,这里是利用rootIsolateSendPort向rootIsolate发送消息,只不过发送消息是newIsolate的SendPort, 这样rootIsolate就能拿到newIsolate的SendPort
        rootIsolateSendPort.send(newIsolateSendPort);
        newIsolateReceivePort.listen((message) {
          
        });
      }
    

    这一步结束,我们常驻isolate已经有了,并且它和我们mainIsolate已经建立起了通信通道

    2.如何使用这个常驻isolate

    首先我想要什么?

    我想实现的就是,给你个函数地址,给你参数,你去干活吧,然后结束之后返回给我结果,伪代码是这样的

    doWork(Function work,params) async {
        newIsolateSendPort?.send([work,params]);
        ...(work执行(耗时))
        return work执行结果
      }
    

    但是我的回调结果在listen里边啊,我怎么做才能让doWork这个函数等我呢?最后在朋友的提示下,看到了Completer,这个问题就迎刃而解了,这个类可以让你的函数停止,然后调用complete,才会继续执行,这不是正是我想要的嘛?等着,然后listen到结果,执行complete,让doWork函数继续进行?perfect!!!
    然后就是一些进一步的优化,

    将通信内容(函数地址,参数,id)对象化,
    ///通知子isolate工作
    class SendMessage {
      int id;
      Map<String,dynamic> params;
      Future Function(Map<String,dynamic> params) work;
    
      SendMessage({
        required this.id,
        required this.params,
        required this.work,
      });
    }
    
    ///通知main isolate,活干完了,给你结果
    class ReceiveMessage {
      int id;
      dynamic result;
    
      ReceiveMessage({
        required this.id,
        this.result,
      });
    }
    

    将常驻isolate表达成单例,最后就是最终的代码

    import 'dart:async';
    import 'dart:isolate';
    
    class GlobalIsolate {
      static Isolate? _newIsolate;
      static SendPort? _newIsolateSendPort;
    
      static int _i = 0;
    
      static final Map<int, dynamic> _works = {};
    
      static Future isolateDo({
        required Future Function(Map<String,dynamic> params) work,
        required Map<String,dynamic> params,
      }) async {
        if (_newIsolate == null) {
          await _initNewIsolate();
        }
        SendMessage message = SendMessage(id: _i, params: params, work: work);
        _newIsolateSendPort?.send(message);
        Completer c = Completer();
        _works[_i] = c;
        _i++;
        try {
          final result = await c.future;
          return result;
        } catch (e) {
          return null;
        }
      }
    
      static Future _initNewIsolate() async {
        if (_newIsolate != null) return;
        //第1步: 默认执行环境下是rootIsolate,所以创建的是一个rootIsolateReceivePort
        ReceivePort rootIsolateReceivePort = ReceivePort();
        //第2步: 获取rootIsolateSendPort
        SendPort rootIsolateSendPort = rootIsolateReceivePort.sendPort;
        //第3步: 创建一个newIsolate实例,并把rootIsolateSendPort作为参数传入到newIsolate中,为的是让newIsolate中持有rootIsolateSendPort, 这样在newIsolate中就能向rootIsolate发送消息了
        _newIsolate = await Isolate.spawn(_createNewIsolateContext,
            rootIsolateSendPort); //注意createNewIsolateContext这个函数执行环境就会变为newIsolate, rootIsolateSendPort就是createNewIsolateContext回调函数的参数
        //第7步: 通过rootIsolateReceivePort接收到来自newIsolate的消息,所以可以注意到这里是await 因为是异步消息
        //只不过这个接收到的消息是newIsolateSendPort, 最后赋值给全局newIsolateSendPort,这样rootIsolate就持有newIsolate的SendPort
        // var message = await rootIsolateReceivePort.first;
        //第8步,建立连接成功
        // print(messageList[0] as String);
        // newIsolateSendPort = message as SendPort;
        // print("newIsolateSendPort $message");
        rootIsolateReceivePort.listen((message) {
          if (message is SendPort) {
            print("newIsolateSendPort $message");
            _newIsolateSendPort = message;
          } else if (message is ReceiveMessage) {
            if (_works[message.id] is Completer) {
              Completer c = _works[message.id];
              c.complete(message.result);
              _works.remove(message.id);
            }
          }
        });
      }
    
    //特别需要注意:createNewIsolateContext执行环境是newIsolate
      static void _createNewIsolateContext(SendPort rootIsolateSendPort) async {
        //第4步: 注意callback这个函数执行环境就会变为newIsolate, 所以创建的是一个newIsolateReceivePort
        ReceivePort newIsolateReceivePort = ReceivePort();
        //第5步: 获取newIsolateSendPort, 有人可能疑问这里为啥不是直接让全局newIsolateSendPort赋值,注意这里执行环境不是rootIsolate
        SendPort newIsolateSendPort = newIsolateReceivePort.sendPort;
        //第6步: 特别需要注意这里,这里是利用rootIsolateSendPort向rootIsolate发送消息,只不过发送消息是newIsolate的SendPort, 这样rootIsolate就能拿到newIsolate的SendPort
        rootIsolateSendPort.send(newIsolateSendPort);
        newIsolateReceivePort.listen((message) {
          if (message is SendMessage) {
            message.work(message.params).then((value) => rootIsolateSendPort
                .send(ReceiveMessage(id: message.id, result: value)));
          }
        });
      }
    }
    
    ///通知子isolate工作
    class SendMessage {
      int id;
      Map<String,dynamic> params;
      Future Function(Map<String,dynamic> params) work;
    
      SendMessage({
        required this.id,
        required this.params,
        required this.work,
      });
    }
    
    ///通知main isolate,活干完了,给你结果
    class ReceiveMessage {
      int id;
      dynamic result;
    
      ReceiveMessage({
        required this.id,
        this.result,
      });
    }
    

    最后测试一下

    test() async {
        await GlobalIsolate.init();
        int a = 10;
    
        final result = await GlobalIsolate.isolateDo(
            params: {"a":a},
            work: (Map<String,dynamic> params) async {
              sleep(Duration(seconds: 2));
              var a= params["a"];
              return {
                "result1":"1 - ${a++}",
                "result2":"2 - ${a++}",
                "result3":"3 - ${a++}",
              };
            }
        );
        print("result = $result");
      }
    
    flutter: newIsolateSendPort SendPort
    flutter: result = {result1: 1 - 10, result2: 2 - 11, result3: 3 - 12}
    

    相关文章

      网友评论

          本文标题:flutter中如何创建并且使用一个常驻isolate

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