flutter

flutter-isolate详解

2021-06-25  本文已影响0人  浮华_du

一. 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关键字实现了异步操作, 而其所谓的异步其实也是运行在同一线程中并没有开启新的线程, 只是通过单线程的任务调度实现一个先执行其他的代码片段,等这边有结果后再返回的异步效果.

举个栗子:

假设有一个任务,需要计算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. 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

上一篇下一篇

猜你喜欢

热点阅读