あまり使ったことは無いけど、一応Flutterのisolateの内容を理解しておく

---
# :book: Flutterの記事を整理し本にしました :book:
https://zenn.dev/kazutxt/books/flutter_practice_introduction

* <font color="red"><b>本稿の記事を含む様々な記事を体系的に整理し本にまとめました</b></font>
* 今後はこちらを最新化するため、<font color="red"><b>最新情報はこちらをご確認くださ</b>い</font>
* 10万文字を超える超大作になっています(笑)

---

# はじめに
* 使ったことはないんだけど、一応isolateをきちんと理解しておく
* 現状は、非同期で間に合っているかな。。。

## まとめ
* [Flutter系の記事のまとめ](https://qiita.com/kazutxt/items/524acb1c134015682ccf)


# Flutterのスレッド

Flutterはシングルスレッド・イベントループという処理方針を取っています。
これは、1つのスレッドがイベントのキューをつくり、それを順番に処理していくという考え方です。

シングルスレッドですべての処理を行おうとすると、CPUが遊んでいる待ち時間に気を使います。
しかし、(既に過去のチャプターで解説したとおり)Flutterには、async/awaitなどを使った非同期処理を行うことができ、シーケンシャルな処理しかできないわけではありません。

ですが一方で、すごく重たい処理を連続で行う場合には、他の処理の進行に影響を及ぼしてしまいます。

# Isolate

上記のような非同期ではなく、多重ループのような重たいCPUを使い続ける処理を行う場合には、Isolateを活用することができます。

Isolateは**分離**という意味です。

基本となるFlutterのMain Isolateとは別管理のリソースをもつIsolateで処理を行うことができます。
ただし、Isolateは互いに"分離"されているので、呼び出し元から呼び出し先のIsolateにアクセスすることはできません。
互いのIsolateはメッセージを送受信して情報をやり取りします。

いつもどおり、HelloWorldのソースを利用しますが、画面はあまり関係ありません。

```dart:main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'dart:isolate';

void main() {
  runApp(MyApp());
}

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

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  Future<void> _incrementCounter() async {
    var recivePort = ReceivePort();
    var sendPort = recivePort.sendPort;
    late Capability capability;

    // 子供からメッセージを受け取る
    recivePort.listen((message) {
      print(message);
      recivePort.close();
    });

    final isolate = await Isolate.spawn(child, sendPort);

    Timer(Duration(seconds: 5), () {
      print("pausing");
      capability = isolate.pause();
    });
    Timer(Duration(seconds: 10), () {
      print("resume");
      isolate.resume(capability);
    });

    Timer(Duration(seconds: 15), () {
      print("kill");
      isolate.kill();
    });

    setState(() {
      _counter++;
    });
  }

  static void child(SendPort sendPort) {
    int i = 0;
    // 親にメッセージを送る
    Timer.periodic(Duration(seconds: 1), (timer) => {sendPort.send(i++)});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
```

まず、`Isolate.spawn(child, sendPort)`は、新しい`isolate`を作っています。
この時、第一引数となる`isolate`のエントリポイントの関数は、staticな関数である必要があります。

`child`関数は、毎秒値をインクリメントしながら親にデータを通知します。
呼び元の関数は、5秒後に一時停止、10秒後に再開、15秒後に終了させています。

それぞれの`isolate`は`recivePort/sendPort`を使い、データをやり取りします。送る方は`send`、受け取る方は`listen`を使って非同期に行っています。

なお、一時停止する際に、`capability`を受け取り、再開する際に`capability`を渡すことで、同じ状況で再開することができます。

```sh:result.sh
I/flutter (23253): 0
I/flutter (23253): 1
I/flutter (23253): 2
I/flutter (23253): 3
I/flutter (23253): pausing
I/flutter (23253): resume
I/flutter (23253): 4
I/flutter (23253): 5
I/flutter (23253): 6
I/flutter (23253): 7
I/flutter (23253): 8
I/flutter (23253): 9
I/flutter (23253): kill
```

一時停止されてから、再開されるまでカウントが止まり、再開時は値が続いていることが確認できます。

# compute
Flutterでは`Isolate`を非同期関数のように扱えるようにcompute関数が用意されています。

```dart:main.dart
void main() { /* 変更が無いため中略 */ }
class MyApp extends StatelessWidget {/* 変更が無いため中略 */ }
class MyHomePage extends StatefulWidget {/* 変更が無いため中略 */}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  Future<void> _incrementCounter() async {
    compute(child, 1).then((result) {
      print(result);
    });
    print("main end");

    setState(() {
      _counter++;
    });
  }

  static int child(int input) {
    sleep(Duration(seconds: 10));
    return input;
  }

  @override
  Widget build(BuildContext context) {/* 変更が無いため中略 */}
}

```
```sh:result.sh
I/flutter (23253): main end
I/flutter (23253): 1
```