flutter-isolate详解

一. 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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容