FutureやStreamによる非同期処理は、I/O待ち(通信やファイル読み書き)をノンブロッキングに行うための仕組みであり、実行自体は依然として同一のシングルスレッド(イベントループ)上で行われます。
そのため、画像処理、暗号化/複合、数万件に及ぶ巨大なJSONのパースなど、CPU負荷の高い重い計算 を実行すると、UIのレンダリングがコマ落ち(ジャンク/Jank)してしまいます。
Dartはマルチコアプロセッサを活用して真の並列計算を行うために、メモリを共有しない独立した実行単位である Isolate(アイソレート) を提供しています。
この章では、Isolateの仕組み、Isolate.run() による簡単な並列化、そしてポートを用いた双方向通信を学びます。
一般的なマルチスレッドプログラミング(JavaやC++など)では、複数のスレッドが同一のヒープメモリ空間を共有するため、ミューテックスやロックによる排他制御が必要となり、レースコンディション(競合状態) や デッドロック の温床になりがちでした。
Dartの Isolate(アイソレート=隔離されたもの) は、独自のヒープメモリと独立したイベントループを持つ完全な隔離環境 です。
+-----------------------------+ +-----------------------------+
| Isolate A | | Isolate B |
| +-----------------------+ | | +-----------------------+ |
| | ヒープメモリ | | | | ヒープメモリ | |
| | (オブジェクト・変数) | | | | (オブジェクト・変数) | |
| +-----------------------+ | | +-----------------------+ |
| +-----------------------+ | | +-----------------------+ |
| | イベントループ | | | | イベントループ | |
| +-----------------------+ | | +-----------------------+ |
+--------------+--------------+ +--------------+--------------+
^ |
| メッセージ通信 (コピー) |
+-------------------------------------+
メモリ空間が完全に分離されているため、ロック不要で安全にマルチコアを活用した並列実行が可能です。
Isolate の概念と Isolate.run() による別スレッド処理Dart 2.19以降では、単発の重い計算処理(CPUバウンドタスク)を別スレッドで実行して結果を受け取るための Isolate.run() が導入されました。
内部で別Isolateの生成、計算の実行、結果メッセージの返却、そしてIsolateの破棄までを自動で行ってくれます(Flutterの compute() 関数と同等です)。
import 'dart:isolate';
// 別Isolateで実行される重い計算関数
int heavyCalculation(int count) {
int sum = 0;
for (int i = 0; i < count; i++) {
sum += i;
}
return sum;
}
void main() async {
print('メインスレッド開始');
// 別スレッドで重い計算を実行
final result = await Isolate.run(() => heavyCalculation(1000000));
print('計算完了: $result');
print('メインスレッド終了');
}dart run isolate_run_demo.dartメインスレッド開始 計算完了: 499999500000 メインスレッド終了
長時間常駐するバックグラウンドワーカーを作成し、メインIsolateとの間で継続的な双方向通信を行うには、ReceivePort と SendPort によるメッセージパッシングを使用します。
import 'dart:isolate';
// ワーカーIsolateのエントリーポイント
void worker(SendPort mainSendPort) {
final workerReceivePort = ReceivePort();
// 自身のSendPortをメインIsolateに送り返す
mainSendPort.send(workerReceivePort.sendPort);
// メインからのメッセージを待ち受ける
workerReceivePort.listen((message) {
if (message is int) {
final squared = message * message;
mainSendPort.send('計算結果: $squared');
}
});
}
void main() async {
final mainReceivePort = ReceivePort();
// ワーカーIsolateを起動
await Isolate.spawn(worker, mainReceivePort.sendPort);
// 最初に応答として送られてくるワーカーのSendPortを取得
final workerSendPort = await mainReceivePort.first as SendPort;
// 新たな受信ポートを作成して結果を待機
final responsePort = ReceivePort();
workerSendPort.send(7);
mainReceivePort.close();
}この章では、Dartにおける並列処理の仕組みである Isolate について学びました。
Isolate で別コアへ逃がす。Isolate.run(): 単発の重い計算タスクをワンライナーで安全に別スレッド実行。ReceivePort / SendPort: 長時間常駐するワーカーとの双方向メッセージ通信。Dartチュートリアルの全章を完走したことで、言語基礎・Null Safety・関数型機能・モダンなDart 3パターン・非同期・Isolate並列処理まで、実践で活躍するための知識が身につきました!
巨大なリストのソートと集計処理を Isolate.run() を使って別スレッドで実行するプログラムを作成してください。
List<int> generateAndSortNumbers(int size) 関数を定義する。
size のランダムな整数リストを生成し、降順ソートして先頭10件を返す。main() で await Isolate.run(() => generateAndSortNumbers(500000)) を呼び出す。import 'dart:isolate';
import 'dart:math';
// ここに関数を定義してください
void main() async {
// ここで動作確認を行ってください
}dart run practice12_1.dart文字列を反転して大文字にするワーカースレッドを作成し、メッセージを送受信してください。
void textProcessor(SendPort mainSendPort) を定義する。
ReceivePort を作成し、その sendPort を mainSendPort に送る。split('').reversed.join())かつ大文字(toUpperCase())にして送り返す。main() で Isolate.spawn(textProcessor, receivePort.sendPort) を起動する。'hello dart' を送信し、ワーカーから 'TRAD OLLEH' が返ってくることを確認してポートを閉じる。import 'dart:isolate';
// ここに関数を定義してください
void main() async {
// ここで動作確認を行ってください
}dart run practice12_2.dart