flutter“多線程”isolate小學生級理解

我們小學二年級就學過:

Dart 是單線程的并且 Flutter 依賴于 Dart赃梧。

但是如果我們要在主線程做一些計算量大的操作,就必然會阻塞線程,使UI更新卡頓甚至卡死。那怎么辦呢肉康?

好消息是 Dart 為我們提供了 isolate,isolate 跟線程差不多灼舍,他是 Dart 中的線程吼和。

isolate 與線程的區(qū)別就是線程與線程之間是共享內(nèi)存的,而 isolate 和 isolate 之間是不共享的骑素,所以叫 isolate (隔離)炫乓。

在flutter 里面主線程就是主 isolate 。如果我們要進行一些大計算量的操作就應該啟動一個新的 isolate砂豌。

那么應該如何來開啟呢厢岂?在此之前我想講個故事光督。

小紅與小藍的故事

有個舞者叫小紅阳距,她正在給觀眾跳舞。

跳舞的小紅

但是觀眾卻要求她一邊跳舞一邊計算一個數(shù)字里面有多少個偶數(shù)结借。于是筐摘。。。

異步計算

這那行翱臁圃酵!你必須給我一邊跳一邊算,算的時候不能停下來馍管!

于是小紅沒辦法郭赐,決定在異世界召喚一個小藍來幫她計算。

image

但是小紅和小藍被異世界的屏障隔離确沸,她們也沒有思想共通的超能力捌锭。只能在召喚的同時傳送一個包裹給小藍

小藍被召喚出來后收到包裹罗捎,打開后里面是要計算的數(shù)字观谦,就開始計算,但是計算后要怎么把結果告訴小紅呢桨菜?

上帝做了一個約定豁状,在小紅召喚小藍的時候,會變一個傳送裝置(傳送裝置可以用來接收包裹倒得,還可以生成一個專屬發(fā)送器)泻红。然后把發(fā)送器傳送給小藍。

當小藍被召喚出來后屎暇,打開包裹承桥,里面是一個發(fā)送器,然后小藍自己也變一個傳送裝置根悼,生成一個發(fā)送器凶异,然后用小紅的發(fā)送器把小藍的發(fā)送器發(fā)送給小紅。發(fā)送出去后就坐在傳送裝置旁邊等包裹挤巡。

當小紅收到小藍的發(fā)送器后就把小藍的發(fā)送器存起來剩彬。

當有觀眾要求小紅計算時,就分神一邊跳舞矿卑,一邊生成一個臨時傳送裝置喉恋,把要計算的數(shù)字和臨時發(fā)送器打包成一個包裹,然后通過小藍的發(fā)送器發(fā)給小藍母廷,等傳送裝置出結果轻黑。因為不用自己算了,只是等琴昆,所以跳舞的時候線條也流暢了氓鄙,動作也優(yōu)美了。

說回小藍這邊业舍,小藍看到傳送裝置出現(xiàn)了一個包裹抖拦,里面是一個臨時發(fā)送器升酣,還有一個數(shù)字。于是小藍就開始計算态罪。算好了就用臨時發(fā)送器把數(shù)字發(fā)送給小紅噩茄。

小紅收到結果后就告訴觀眾,那個數(shù)字有多少個偶數(shù)复颈。

故事結束绩聘,第一次嘗試這樣的風格,可能寫得有點爛耗啦,不過結合代碼來看的話君纫,應該還是挺容易理解的。

代碼實踐

首先我們先讓小紅跳起舞來芹彬。

  @override
  void initState() {
    controller =
        AnimationController(duration: Duration(seconds: 3), vsync: this);
    animation = Tween<double>(begin: 0, end: pi * 2).animate(controller);
    controller.repeat();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            AnimatedBuilder(
                animation: animation,
                child: Text(
                  '小紅',
                  style: TextStyle(fontSize: 30, color: Colors.red),
                ),
                builder: (context, child) {
                  return Transform.rotate(
                    angle: animation.value,
                    child: child,
                  );
                }),
          ],
        ),
      ),
    );
  }

接下來讓小紅計算一個數(shù)字里面有多少個偶數(shù)蓄髓。

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text(widget.title),
    ),
    body: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          AnimatedBuilder(
              animation: animation,
              child: Text(
                '小紅',
                style: TextStyle(fontSize: 30, color: Colors.red),
              ),
              builder: (context, child) {
                return Transform.rotate(
                  angle: animation.value,
                  child: child,
                );
              }),
          Padding(
            padding: EdgeInsets.only(top: 16),
            child:
                RaisedButton(onPressed: count, child: Text('異步計算偶數(shù)的個數(shù)')),
          ),
          Text(result)
        ],
      ),
    ),
  );
}

int getRandom() {
  int a =  Random().nextInt(100);
  return a + 1000000000;
}

// 異步計算
count() async {
  int random = getRandom();
  int r = countEven(random);
  setState(() {
    this.result = '${random.toString()}有${r.toString()}個偶數(shù)';
  });
}

//計算偶數(shù)的個數(shù)
int countEven(num num) {
  int count = 0;
  while (num > 0) {
    if (num % 2 == 0) {
      count++;
    }
    num--;
  }
  return count;
}

這就是效果

異步計算

定義 isolate

我愿稱之為召喚小藍。

首先我們要知道兩個類:

ReceivePort
SendPort

ReceivePort 就是故事中的傳送裝置舒帮,而 SendPort 則是發(fā)送器会喝。

我們可以通過以下方式創(chuàng)建傳送裝置和對應的發(fā)送器

ReceivePort receive = ReceivePort();
SendPort sender = receive.sendPort;

好的,知道這些就行了玩郊。接下來我們定義小藍肢执。

// 消息包裹,用來存臨時發(fā)送器和消息
class MessagePackage {
  SendPort sender; // 臨時發(fā)送器
  dynamic msg; // 消息

  MessagePackage(this.sender, this.msg);

}

// 我是小藍译红,負責計算偶數(shù)的個數(shù),我必須是頂級函數(shù)
blueCounter(SendPort redSendPort) {
  // 創(chuàng)建小藍的傳送裝置
  ReceivePort blueReceivePort = ReceivePort();
  // 用小紅的發(fā)送器把小藍的發(fā)送器發(fā)送出去
  redSendPort.send(blueReceivePort.sendPort);
  // 監(jiān)聽小藍的傳送裝置预茄,等待小紅叫小藍計算
  blueReceivePort.listen((package) {
    // 這里的msg是dynamic,需要轉換成 MessagePackage 類侦厚,上面自己定義的包裹封裝類
    MessagePackage _msg = package as MessagePackage;
    // 小藍開始計算
    int r = countEven(_msg.msg as num);
    // 計算好了用小紅的臨時發(fā)送器告訴小紅
    _msg.sender.send(r);
  });
}

創(chuàng)建isolate

工具人小藍定義好了耻陕,我們?nèi)コ跏蓟?召喚)一下小藍。

// 創(chuàng)建isolate
void createIsolate() async {
  // 創(chuàng)建小紅的接收器刨沦,用來接收小藍的發(fā)送器
  ReceivePort redReceive = ReceivePort();
  // 創(chuàng)建 isolate诗宣, 并且把小紅的發(fā)送器傳給小藍
  isolate = await Isolate.spawn<SendPort>(blueCounter, redReceive.sendPort);
  // 等待小藍把發(fā)送器發(fā)送給小紅
  blueSender = await redReceive.first;
  // 不用了記得關閉接收器
  redReceive.close();
}


@override
void initState() {
  controller =
      AnimationController(duration: Duration(seconds: 3), vsync: this);
  animation = Tween<double>(begin: 0, end: pi * 2).animate(controller);
  controller.repeat();
  // 在initState中初始化isolate
  createIsolate();
  super.initState();
}

現(xiàn)在小藍已經(jīng)被召喚了出來,并且和小紅建立了通信想诅。

使isolate 開始計算

接下來我們就讓小紅開始計算吧召庞。


@override
Widget build(BuildContext context) {
  ...
  Padding(
      padding: EdgeInsets.only(top: 16),
      child: RaisedButton(
          onPressed: isolateCount, child: Text('isolate計算偶數(shù)的個數(shù)')
      ),
  ),
  ...
}

// 開啟isolate計算
isolateCount() async {
  // 獲取要計算的數(shù)字
  int random = getRandom();
  // 創(chuàng)建一個臨時傳送裝置
  ReceivePort _temp = ReceivePort();
  // 用小藍的發(fā)送裝置發(fā)送一個消息包裹,里面是臨時傳送裝置的發(fā)送器和要計算的數(shù)字
  blueSender.send(MessagePackage(_temp.sendPort, random));
  // 等待臨時傳送裝置返回計算結果
  int r = await _temp.first;
  // 不用了記得關閉臨時接收器
  _temp.close();
  // 把計算結果告訴觀眾
  setState(() {
    this.result = '${random.toString()}有${r.toString()}個偶數(shù)';
  });
}

需要注意的是當使用完了 isolate 記得要銷毀来破。


@override
void dispose() {
  // 銷毀 isolate
  isolate?.kill(priority: Isolate.immediate);
  super.dispose();
}

OK,到這里相信你已經(jīng)看懂并會使用 isolate 了篮灼。
我們來看看效果圖。

isolate計算

使用 computed

到這里還沒完徘禁,也許你會覺得太麻煩了诅诱。是的這樣用 isolate 太麻煩了,isolate 被設計成可以多次輸入輸出晌坤,而我們做這個計算只有一次輸入和輸出逢艘,那么我們就可以用 flutter 為我們提供的 computed 來完成計算操作,它是對 isolate 的一個封裝骤菠。下面看看怎么用吧它改!敲簡單的。

import 'dart:isolate';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:math';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'isolate Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'isolate Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation<double> animation;
  String result = '';
  SendPort blueSender;
  Isolate isolate;

  @override
  void initState() {
    controller =
        AnimationController(duration: Duration(seconds: 3), vsync: this);
    animation = Tween<double>(begin: 0, end: pi * 2).animate(controller);
    controller.repeat();
    // 在initState中初始化isolate
    createIsolate();
    super.initState();
  }

  @override
  void dispose() {
    // 銷毀 isolate
    isolate?.kill(priority: Isolate.immediate);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            AnimatedBuilder(
                animation: animation,
                child: Text(
                  '小紅',
                  style: TextStyle(fontSize: 30, color: Colors.red),
                ),
                builder: (context, child) {
                  return Transform.rotate(
                    angle: animation.value,
                    child: child,
                  );
                }),
            Padding(
              padding: EdgeInsets.only(top: 16),
              child: RaisedButton(onPressed: count, child: Text('異步計算偶數(shù)的個數(shù)')),
            ),
            Padding(
              padding: EdgeInsets.only(top: 16),
              child: RaisedButton(
                  onPressed: isolateCount, child: Text('isolate計算偶數(shù)的個數(shù)')),
            ),
            Padding(
              padding: EdgeInsets.only(top: 16),
              child: RaisedButton(
                  onPressed: computeCount, child: Text('compute計算偶數(shù)的個數(shù)')),
            ),
            Text(result)
          ],
        ),
      ),
    );
  }

  // 獲取隨機數(shù)
  int getRandom() {
    int a = Random().nextInt(100);
    return a + 1000000000;
  }

  // 異步計算
  count() async {
    int random = getRandom();
    int r = countEven(random);
    setState(() {
      this.result = '${random.toString()}有${r.toString()}個偶數(shù)';
    });
  }

  // 創(chuàng)建isolate
  void createIsolate() async {
    // 創(chuàng)建小紅的接收器商乎,用來接收小藍的發(fā)送器
    ReceivePort redReceive = ReceivePort();
    // 創(chuàng)建 isolate央拖, 并且把小紅的發(fā)送器傳給小藍
    isolate = await Isolate.spawn<SendPort>(blueCounter, redReceive.sendPort);
    // 等待小藍把發(fā)送器發(fā)送給小紅
    blueSender = await redReceive.first;
    // 不用了記得關閉接收器
    redReceive.close();
  }

  // 利用compute計算
  computeCount() async {
    int random = getRandom();
    // compute 的回調(diào)函數(shù)必須是頂級函數(shù)或者static函數(shù)
    int r = await compute(countEven, random);
    setState(() {
      this.result = '${random.toString()}有${r.toString()}個偶數(shù)';
    });
  }

  // 開啟isolate計算
  isolateCount() async {
    // 獲取要計算的數(shù)字
    int random = getRandom();
    // 創(chuàng)建一個臨時傳送裝置
    ReceivePort _temp = ReceivePort();
    // 用小藍的發(fā)送裝置發(fā)送一個消息包裹,里面是臨時傳送裝置的發(fā)送器和要計算的數(shù)字
    blueSender.send(MessagePackage(_temp.sendPort, random));
    // 等待臨時傳送裝置返回計算結果
    int r = await _temp.first;
    _temp.close();
    // 把計算結果告訴觀眾
    setState(() {
      this.result = '${random.toString()}有${r.toString()}個偶數(shù)';
    });
  }
}

// 消息包裹鹉戚,用來存臨時發(fā)送器和消息
class MessagePackage {
  SendPort sender; // 臨時發(fā)送器
  dynamic msg; // 消息

  MessagePackage(this.sender, this.msg);
}

// 我是小藍鲜戒,負責計算偶數(shù)的個數(shù),我必須是頂級函數(shù)
blueCounter(SendPort redSendPort) {
  // 創(chuàng)建小藍的傳送裝置
  ReceivePort blueReceivePort = ReceivePort();
  // 用小紅的發(fā)送器把小藍的發(fā)送器發(fā)送出去
  redSendPort.send(blueReceivePort.sendPort);
  // 監(jiān)聽小藍的傳送裝置,等待小紅叫小藍計算
  blueReceivePort.listen((package) {
    // 這里的msg是dynamic抹凳,需要轉換成 MessagePackage 類遏餐,上面自己定義的包裹封裝類
    MessagePackage _msg = package as MessagePackage;
    // 小藍開始計算
    int r = countEven(_msg.msg as num);
    // 計算好了用小紅的臨時發(fā)送器告訴小紅
    _msg.sender.send(r);
  });
}

//計算偶數(shù)的個數(shù),此函數(shù)需要大量的計算資源和時間
int countEven(num num) {
  int count = 0;
  while (num > 0) {
    if (num % 2 == 0) {
      count++;
    }
    num--;
  }
  return count;
}
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赢底,一起剝皮案震驚了整個濱河市失都,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌幸冻,老刑警劉巖粹庞,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異洽损,居然都是意外死亡庞溜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門碑定,熙熙樓的掌柜王于貴愁眉苦臉地迎上來流码,“玉大人,你說我怎么就攤上這事延刘÷玫啵” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵访娶,是天一觀的道長商虐。 經(jīng)常有香客問我,道長崖疤,這世上最難降的妖魔是什么秘车? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮劫哼,結果婚禮上叮趴,老公的妹妹穿的比我還像新娘。我一直安慰自己权烧,他們只是感情好眯亦,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布伤溉。 她就那樣靜靜地躺著,像睡著了一般妻率。 火紅的嫁衣襯著肌膚如雪乱顾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天宫静,我揣著相機與錄音走净,去河邊找鬼。 笑死孤里,一個胖子當著我的面吹牛伏伯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捌袜,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼说搅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了虏等?” 一聲冷哼從身側響起蜓堕,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎博其,沒想到半個月后套才,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡慕淡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年背伴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片峰髓。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡傻寂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出携兵,到底是詐尸還是另有隱情疾掰,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布徐紧,位于F島的核電站静檬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏并级。R本人自食惡果不足惜拂檩,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嘲碧。 院中可真熱鬧稻励,春花似錦、人聲如沸愈涩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至煤篙,卻和暖如春斟览,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背舰蟆。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留狸棍,地道東北人身害。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像草戈,于是被迫代替她去往敵國和親塌鸯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

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

  • 該文章屬于劉小壯原創(chuàng)唐片,轉載請注明:劉小壯[http://www.reibang.com/u/2de707c93d...
    劉小壯閱讀 30,123評論 12 86
  • Dart之異步編程 本文轉載自https://www.cnblogs.com/lxlx1798/p/1112656...
    緣煥閱讀 1,327評論 0 1
  • 風吹動香樟樹的樹葉丙猬,空氣里傳來熟悉的樹葉的清香,林子戴著耳機躺在足球場的草坪上聽歌费韭,午后的陽光格外溫和茧球,即便如此,...
    拾月言人閱讀 229評論 0 0
  • 這天女貞路四號的早餐桌上又起了爭執(zhí)星持。一大早抢埋,弗農(nóng)·德思禮先生就被他的外甥哈利屋里的一陣高校怪聲吵醒了《皆荩“這星期是...
    聶亞卓閱讀 169評論 0 0
  • 時間安排 周一周三周五 周二四及奶奶出差
    chzhbob閱讀 202評論 0 0