flutter-isolate詳解

一. isolate簡介

Dart 是單線程绸狐,Dart 為我們提供了 isolate,isolate 跟線程差不多驯耻,它可以理解為 Dart 中的線程养叛。isolate 與線程的區(qū)別就是線程與線程之間是共享內(nèi)存的,而 isolate 和 isolate 之間是內(nèi)存不共享的狠半,所以叫 isolate (隔離)噩死。因此也不存在鎖競(jìng)爭(zhēng)問題,兩個(gè)Isolate完全是兩條獨(dú)立的執(zhí)行線神年,且每個(gè)Isolate都有自己的事件循環(huán)已维,它們之間只能通過發(fā)送消息通信,所以它的資源開銷低于線程已日。

原文鏈接:https://blog.csdn.net/u011578734/article/details/108853613
大多數(shù)計(jì)算機(jī)中垛耳,甚至在移動(dòng)平臺(tái)上,都在使用多核 CPU。為了有效利用多核性能堂鲜,開發(fā)者一般使用共享內(nèi)存的方式讓線程并發(fā)地運(yùn)行栈雳。然而,多線程共享數(shù)據(jù)通常會(huì)導(dǎo)致很多潛在的問題缔莲,并導(dǎo)致代碼運(yùn)行出錯(cuò)哥纫。
為了解決多線程帶來的并發(fā)問題,Dart 使用 isolates 替代線程痴奏,所有的 Dart 代碼均運(yùn)行在一個(gè) isolates 中蛀骇。每一個(gè) isolates 有它自己的堆內(nèi)存以確保其狀態(tài)不被其它 isolates 訪問。
每個(gè) isolate 都擁有自己的事件循環(huán)及隊(duì)列(MicroTask 和 Event)读拆。這意味著在一個(gè) isolate 中運(yùn)行的代碼與另外一個(gè) isolate 不存在任何關(guān)聯(lián)擅憔。
isolate是Dart對(duì)actor并發(fā)模式的實(shí)現(xiàn)。運(yùn)行中的Dart程序由一個(gè)或多個(gè)actor組成檐晕,這些actor也就是Dart概念里面的isolate暑诸。isolate是有自己的內(nèi)存和單線程控制的運(yùn)行實(shí)體。isolate本身的意思是“隔離”棉姐,因?yàn)閕solate之間的內(nèi)存在邏輯上是隔離的屠列。isolate中的代碼是按順序執(zhí)行的,任何Dart程序的并發(fā)都是運(yùn)行多個(gè)isolate的結(jié)果伞矩。因?yàn)镈art沒有共享內(nèi)存的并發(fā)笛洛,沒有競(jìng)爭(zhēng)的可能性所以不需要鎖,也就不用擔(dān)心死鎖的問題乃坤。

二. isolate與async關(guān)系:

之前介紹過 async/await Future的原理, async關(guān)鍵字實(shí)現(xiàn)了異步操作, 而其所謂的異步其實(shí)也是運(yùn)行在同一線程中并沒有開啟新的線程, 只是通過單線程的任務(wù)調(diào)度實(shí)現(xiàn)一個(gè)先執(zhí)行其他的代碼片段苛让,等這邊有結(jié)果后再返回的異步效果.

  • Isolate可以實(shí)現(xiàn)異步并行多個(gè)任務(wù)
  • Future實(shí)現(xiàn)異步串行多個(gè)任務(wù)

舉個(gè)栗子:

假設(shè)有一個(gè)任務(wù),需要計(jì)算1+2+...100的和
我們通常會(huì)這么寫:


class IsolateTestPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return IsolateTestPageState();
  }
}

class IsolateTestPageState extends State<IsolateTestPage> {
  var content = "點(diǎn)擊計(jì)算按鈕,開始計(jì)算";

  @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('計(jì)算'),
                onPressed: () {
                  int result = sum(100);
                  content = "總和$result";
                  setState(() {});
                },
              )
            ],
          ),
        ),
      ),
    );
  }
//計(jì)算0到 num 數(shù)值的總和
  int sum(int num) {
    int count = 0;
    while (num > 0) {
      count = count + num;
      num--;
    }
    return count;
  }
}

試一下,結(jié)果是可以的;但是如果我們改為計(jì)算1+2+...100000000000的和,運(yùn)行代碼會(huì)發(fā)現(xiàn),直接卡死了
這時(shí)候就需要?jiǎng)?chuàng)建一個(gè)isolate來執(zhí)行這個(gè)耗時(shí)的任務(wù),而不影響其他任務(wù)的執(zhí)行. 那么首先,如何創(chuàng)建一個(gè)isolate?

三.創(chuàng)建isolate 及 isolate通信

  • (1).獲取當(dāng)前main isolate的ReceivePort及SendPort
  • (2).使用Isolate.spawn創(chuàng)建新的isolate,需要傳入新的isolate需要完成的任務(wù)名稱及創(chuàng)建者(main isolate)的sendPort. (用于將新的isolate的sendPort傳遞給創(chuàng)建者)
  • (3).在任務(wù)方法中,獲取新的isolate的ReceivePort及SendPort
  • (4).將新的isolate的SendPort,通過main isolate的sendPort,發(fā)送給main isolate,使新的isolate的SendPort能在main isolate中發(fā)送消息
  • (5). SendPort發(fā)送消息, ReceivePort接收消息,互相通信

1. receivePort.listen((message) {}) 能收到其對(duì)應(yīng)的sendPort發(fā)送的消息

_testIsolate() async {
    ReceivePort rp1 = new ReceivePort();
    SendPort port1 = rp1.sendPort;
    // 通過spawn新建一個(gè)isolate,并綁定靜態(tài)方法
    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的發(fā)送器port2
      } else {
        if (port2 != null) {
          print("port2 發(fā)送消息");
          port2?.send([1, "這條信息是 port2 在main isolate中 發(fā)送的"]); // 8.port2發(fā)送消息
        }
      }
    });

    print("port1--main isolate發(fā)送消息");
    port1.send([1, "這條信息是 port1 在main isolate中 發(fā)送的"]); //1.port1發(fā)送消息

    // newIsolate.kill();
  }

// 新的isolate中可以處理耗時(shí)任務(wù)
  static void doWork(SendPort port1) {
    ReceivePort rp2 = new ReceivePort();
    SendPort port2 = rp2.sendPort;
    rp2.listen((message) {
      //9.10 rp2收到消息
      print("rp2 收到消息: $message");
    });
    // 將新isolate中創(chuàng)建的SendPort發(fā)送到main isolate中用于通信
    print("port1--new isolate發(fā)送消息");
    port1.send([0, port2]); //3.port1發(fā)送消息,傳遞[0,rp2的發(fā)送器]
    // 模擬耗時(shí)5秒
    sleep(Duration(seconds: 5));
    print("port1--new isolate發(fā)送消息");
    port1.send([1, "這條信息是 port1 在new isolate中 發(fā)送的"]); //5.port1發(fā)送消息
    print("port2--new isolate發(fā)送消息");
    port2.send([1, "這條信息是 port2 在new isolate中 發(fā)送的"]); //6.port2發(fā)送消息
  }

//I/flutter (14639): port1--main isolate發(fā)送消息
//I/flutter (14639): rp1 收到消息: [1, 這條信息是 port1 在main isolate中 發(fā)送的]
//I/flutter (14639): port1--new isolate發(fā)送消息
//I/flutter (14639): rp1 收到消息: [0, SendPort]
//I/flutter (14639): port1--new isolate發(fā)送消息
//I/flutter (14639): port2--new isolate發(fā)送消息
//I/flutter (14639): rp1 收到消息: [1, 這條信息是 port1 在new isolate中 發(fā)送的]
//I/flutter (14639): port2 發(fā)送消息
//I/flutter (14639): rp2 收到消息: [1, 這條信息是 port2 在new isolate中 發(fā)送的]
//I/flutter (14639): rp2 收到消息: [1, 這條信息是 port2 在main isolate中 發(fā)送的]

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

 _testIsolate() async {
    ReceivePort rp1 = new ReceivePort();
    SendPort port1 = rp1.sendPort;
    // 通過spawn新建一個(gè)isolate湿诊,并綁定靜態(tài)方法
    Isolate newIsolate = await Isolate.spawn(doWork, port1);

    SendPort port2;
    dynamic receiveMsg = await rp1.first; //只拿到第一條收到結(jié)果
    print('rp1 收到消息--$receiveMsg');
    if (receiveMsg is SendPort) {
      SendPort port2 = receiveMsg;
      // print('rp1 收到消息--port2');
      port2.send([1, "這條信息是 port2 在main isolate中 發(fā)送的"]);
    }

    // newIsolate.kill();
  }

// 新的isolate中可以處理耗時(shí)任務(wù)
  static void doWork(SendPort port1) {
    ReceivePort rp2 = new ReceivePort();
    SendPort port2 = rp2.sendPort;
    rp2.listen((message) {
      print("rp2 收到消息-- $message");
    });
    // 將新isolate中創(chuàng)建的SendPort發(fā)送到main isolate中用于通信
    print("port1--new isolate發(fā)送消息--port2");
    port1.send(port2);
    // 模擬耗時(shí)5秒
    sleep(Duration(seconds: 5));
    print("port1--new isolate發(fā)送消息--啊哈哈");
    port1.send("啊哈哈");
  }

3.解決示例問題

知道怎么創(chuàng)建isolate了,我們?cè)倏丛趺磥碛?jì)算1+2+...100000000000的和

(1)使用listen監(jiān)聽,結(jié)果回調(diào)的形式

1.創(chuàng)建isolate
2.打通兩個(gè)isolate的通道(能互相發(fā)送消息)
3.main isolate將要計(jì)算的最大數(shù)傳遞給new isolate; newisolate計(jì)算,計(jì)算完成后,將結(jié)果發(fā)送回 main isolate

calculation(int n, Function(int result) success) async {
    //創(chuàng)建一個(gè)ReceivePort
    final receivePort1 = new ReceivePort();
    //創(chuàng)建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);
      }
    });
  }

  //創(chuàng)建isolate必須要的參數(shù)
  static void createIsolate(SendPort sendPort1) {
    final receivePort2 = new ReceivePort();
    //綁定
    print("sendPort1發(fā)送消息--sendPort2");
    sendPort1.send(receivePort2.sendPort);
    //監(jiān)聽
    receivePort2.listen((message) {
      //獲取數(shù)據(jù)并解析
      print("receivePort2接收到消息--$message");
      if (message is int) {
        num result = summ(message);
        sendPort1.send(result);
      }
    });
  }

  //計(jì)算0到 num 數(shù)值的總和
  static num summ(int num) {
    int count = 0;
    while (num > 0) {
      count = count + num;
      num--;
    }
    return count;
  }

(2)receivePort.first只能收到第一條消息; 我們可以再創(chuàng)建一個(gè)ReceivePort用來傳遞消息,如下:

static Future<dynamic> calculation(int n) async {
    //創(chuàng)建一個(gè)ReceivePort
    final receivePort1 = new ReceivePort();
    //創(chuàng)建isolate
    Isolate isolate = await Isolate.spawn(createIsolate, receivePort1.sendPort);

    //使用 receivePort1.first 獲取sendPort1發(fā)送來的數(shù)據(jù)
    final sendPort2 = await receivePort1.first as SendPort;
    print("receivePort1接收到消息--sendPort2");
    //接收消息的ReceivePort
    final answerReceivePort = new ReceivePort();
    print("sendPort2發(fā)送消息--[$n,answerSendPort]");
    sendPort2.send([n, answerReceivePort.sendPort]);
    //獲得數(shù)據(jù)并返回
    num result = await answerReceivePort.first;
    print("answerReceivePort接收到消息--計(jì)算結(jié)果$result");
    return result;
  }

  //創(chuàng)建isolate必須要的參數(shù)
  static void createIsolate(SendPort sendPort1) {
    final receivePort2 = new ReceivePort();
    //綁定
    print("sendPort1發(fā)送消息--sendPort2");
    sendPort1.send(receivePort2.sendPort);
    //監(jiān)聽
    receivePort2.listen((message) {
      //獲取數(shù)據(jù)并解析
      print("receivePort2接收到消息--$message");
      final n = message[0] as num;
      final send = message[1] as SendPort;
      //返回結(jié)果
      num result = summ(n);
      print("answerSendPort發(fā)送消息--計(jì)算結(jié)果$result");
      send.send(result);
    });
  }

  //計(jì)算0到 num 數(shù)值的總和
  static num summ(int num) {
    int count = 0;
    while (num > 0) {
      count = count + num;
      num--;
    }
    return count;
  }

4. isolate的暫停 恢復(fù) 結(jié)束

    //恢復(fù) isolate 的使用
    isolate.resume(isolate.pauseCapability);

    //暫停 isolate 的使用
    isolate.pause(isolate.pauseCapability);

    //結(jié)束 isolate 的使用
    isolate.kill(priority: Isolate.immediate);

    //賦值為空 便于內(nèi)存及時(shí)回收
    isolate = null;

四.flutter中創(chuàng)建isolate---compute()方法

TextButton(
                child: Text('flutter創(chuàng)建isolate'),
                onPressed: () async {
                  num result = await compute(summ, 10000000000);
                  content = "計(jì)算結(jié)果$result";
                  setState(() {});
                },
              ),

到這里,希望大家對(duì)isolate有更深一步的了解.歡迎點(diǎn)點(diǎn)小心心哦~

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末狱杰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子厅须,更是在濱河造成了極大的恐慌仿畸,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件朗和,死亡現(xiàn)場(chǎng)離奇詭異错沽,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)眶拉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門千埃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人忆植,你說我怎么就攤上這事放可≮司剩” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵耀里,是天一觀的道長蜈缤。 經(jīng)常有香客問我,道長备韧,這世上最難降的妖魔是什么劫樟? 我笑而不...
    開封第一講書人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮织堂,結(jié)果婚禮上叠艳,老公的妹妹穿的比我還像新娘。我一直安慰自己易阳,他們只是感情好附较,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著潦俺,像睡著了一般拒课。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上事示,一...
    開封第一講書人閱讀 49,985評(píng)論 1 291
  • 那天早像,我揣著相機(jī)與錄音,去河邊找鬼肖爵。 笑死卢鹦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的劝堪。 我是一名探鬼主播冀自,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼秒啦!你這毒婦竟也來了熬粗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤余境,失蹤者是張志新(化名)和其女友劉穎驻呐,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芳来,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡暴氏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绣张。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡关带,死狀恐怖侥涵,靈堂內(nèi)的尸體忽然破棺而出沼撕,到底是詐尸還是另有隱情,我是刑警寧澤芜飘,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布务豺,位于F島的核電站,受9級(jí)特大地震影響嗦明,放射性物質(zhì)發(fā)生泄漏笼沥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一娶牌、第九天 我趴在偏房一處隱蔽的房頂上張望奔浅。 院中可真熱鬧,春花似錦诗良、人聲如沸汹桦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舞骆。三九已至,卻和暖如春径荔,著一層夾襖步出監(jiān)牢的瞬間督禽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來泰國打工总处, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留狈惫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓辨泳,卻偏偏與公主長得像虱岂,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容