Skip to content

Isolate 并发编程

Dart 是单线程语言,所有代码默认运行在主 Isolate 中。当遇到耗时计算时,如果直接在主 Isolate 中执行,会导致 UI 卡顿。Isolate 是 Dart 提供的并发方案,可以让耗时任务在独立线程中运行,不阻塞 UI。

Isolate 是什么?

Isolate 可以理解为 Dart 中的"线程",但它与操作系统线程有重要区别:

特性Isolate操作系统线程
内存各自独立,不共享内存共享内存
通信通过消息传递(SendPort / ReceivePort)通过共享内存
不需要锁需要加锁防止竞态
开销比线程轻量较重
┌─────────────────────────────────┐    ┌─────────────────────────────────┐
│         主 Isolate              │    │         子 Isolate              │
│  ┌───────────┐  ┌───────────┐  │    │  ┌───────────┐  ┌───────────┐  │
│  │  UI 渲染   │  │ 事件处理  │  │    │  │ 耗时计算   │  │ 文件解析  │  │
│  └───────────┘  └───────────┘  │    │  └───────────┘  └───────────┘  │
│                                 │    │                                 │
│  ReceivePort ◄──── 消息 ──── SendPort │  SendPort ──── 消息 ────► ReceivePort │
└─────────────────────────────────┘    └─────────────────────────────────┘

核心概念

Isolate 之间不共享内存,只能通过 SendPort / ReceivePort 传递消息。这是 Isolate 与线程最大的区别,也是它不需要锁的原因。

何时需要 Isolate?

需要 Isolate 的场景

  • 解析大型 JSON(如几 MB 的配置文件)
  • 图片处理(压缩、裁剪、滤镜)
  • 加密 / 解密操作
  • 大量数学计算(排序、搜索)
  • 文件读写(大文件)

不需要 Isolate 的场景

  • 网络请求 —— http / dio 内部已异步处理,不阻塞 UI
  • 简单的数据转换 —— json.decode 小数据量很快
  • 读写 SharedPreferences —— 数据量小,速度够快
  • 数据库操作 —— 大多数 ORM 已异步封装

如何判断?

如果耗时操作超过 16 毫秒(一帧的时间),就可能影响 UI 流畅度,应考虑使用 Isolate。

Isolate.spawn:创建子 Isolate

Isolate.spawn 是最基础的创建 Isolate 方式。

基本用法

dart
import 'dart:isolate';

// 子 Isolate 的入口函数
// 必须是顶层函数或静态方法
void heavyTask(SendPort sendPort) {
  int result = 0;
  for (int i = 0; i < 100000000; i++) {
    result += i;
  }
  sendPort.send(result);  // 通过 SendPort 发送结果
}

Future<void> main() async {
  // 1. 创建 ReceivePort 接收消息
  final receivePort = ReceivePort();

  // 2. 创建子 Isolate
  await Isolate.spawn(heavyTask, receivePort.sendPort);

  // 3. 监听子 Isolate 发来的消息
  receivePort.listen((message) {
    print('计算结果: $message');  // 计算结果: 4999999950000000
    receivePort.close();         // 用完关闭
  });
}

双向通信

子 Isolate 也可以向主 Isolate 发送消息,需要两对 SendPort / ReceivePort:

dart
import 'dart:isolate';

void worker(SendPort mainSendPort) {
  // 子 Isolate 创建自己的 ReceivePort
  final workerReceivePort = ReceivePort();
  // 把自己的 SendPort 发给主 Isolate
  mainSendPort.send(workerReceivePort.sendPort);

  // 监听主 Isolate 发来的消息
  workerReceivePort.listen((message) {
    if (message is String && message == 'start') {
      final result = doHeavyWork();
      mainSendPort.send(result);
    }
  });
}

int doHeavyWork() {
  int sum = 0;
  for (int i = 0; i < 100000000; i++) {
    sum += i;
  }
  return sum;
}

Future<void> main() async {
  final mainReceivePort = ReceivePort();

  await Isolate.spawn(worker, mainReceivePort.sendPort);

  // 先收到子 Isolate 的 SendPort
  SendPort? workerSendPort;

  mainReceivePort.listen((message) {
    if (message is SendPort) {
      // 第一次收到的是子 Isolate 的 SendPort
      workerSendPort = message;
      // 向子 Isolate 发送任务
      workerSendPort!.send('start');
    } else {
      // 收到计算结果
      print('计算结果: $message');
      mainReceivePort.close();
    }
  });
}

握手模式

双向通信的标准模式:主 Isolate 先把自己的 SendPort 传给子 Isolate,子 Isolate 再把它的 SendPort 回传。这叫做"握手",是 Isolate 双向通信的基础。

compute:简化版 Isolate

Flutter 提供了 compute 函数,它是 Isolate.spawn 的高层封装,使用更简单。

基本用法

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

// 耗时函数
int heavyCalculation(int limit) {
  int sum = 0;
  for (int i = 0; i < limit; i++) {
    sum += i;
  }
  return sum;
}

Future<void> main() async {
  // compute(函数, 参数) —— 一步到位
  final result = await compute(heavyCalculation, 100000000);
  print('计算结果: $result');
}

compute vs Isolate.spawn

特性computeIsolate.spawn
使用难度简单,一行代码需要手动管理 Port
返回值直接返回结果需要通过 Port 接收
双向通信不支持支持
多次通信不支持支持
生命周期控制自动管理需手动关闭
适用场景一次性计算任务复杂的持续通信场景

推荐

  • 大多数场景用 compute —— 简单高效,自动管理生命周期
  • 需要双向通信或多次交互时用 Isolate.spawn —— 更灵活

在 Widget 中使用 compute

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

// 耗时计算函数(必须是顶层函数或静态方法)
List<int> sortLargeList(List<int> data) {
  data.sort();
  return data;
}

class SortPage extends StatefulWidget {
  const SortPage({super.key});

  @override
  State<SortPage> createState() => _SortPageState();
}

class _SortPageState extends State<SortPage> {
  List<int> _data = [];
  bool _isSorting = false;

  Future<void> _sortData() async {
    setState(() => _isSorting = true);

    // 生成大量随机数据
    final rawData = List.generate(1000000, (i) => (i * 7 + 13) % 1000000);

    // 使用 compute 在子 Isolate 中排序
    final sorted = await compute(sortLargeList, rawData);

    setState(() {
      _data = sorted.take(20).toList();  // 只展示前 20 条
      _isSorting = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Isolate 排序')),
      body: Column(
        children: [
          ElevatedButton(
            onPressed: _isSorting ? null : _sortData,
            child: _isSorting
                ? const CircularProgressIndicator()
                : const Text('排序 100 万条数据'),
          ),
          Expanded(
            child: ListView.builder(
              itemCount: _data.length,
              itemBuilder: (context, index) {
                return ListTile(title: Text('第 ${index + 1} 项: ${_data[index]}'));
              },
            ),
          ),
        ],
      ),
    );
  }
}

实战:JSON 解析不卡 UI

解析大型 JSON 文档是 Isolate 最常见的应用场景之一。

问题:主 Isolate 解析大 JSON 会卡 UI

dart
// ❌ 不推荐:大 JSON 在主 Isolate 中解析,UI 会卡顿
Future<void> loadData() async {
  final jsonString = await rootBundle.loadString('assets/large_data.json');
  // 如果 JSON 有几 MB,这里会卡住 UI
  final data = jsonDecode(jsonString);
  setState(() => _items = data);
}

解决:用 compute 解析

dart
// ✅ 推荐:在子 Isolate 中解析,UI 不卡顿

// 顶层函数:在子 Isolate 中执行 JSON 解析
Map<String, dynamic> parseJson(String jsonString) {
  return jsonDecode(jsonString);
}

Future<void> loadData() async {
  final jsonString = await rootBundle.loadString('assets/large_data.json');
  // 在子 Isolate 中解析
  final data = await compute(parseJson, jsonString);
  setState(() => _items = data);
}

封装为通用工具

dart
import 'dart:convert';
import 'package:flutter/foundation.dart';

/// 在 Isolate 中解析 JSON
Future<T> parseJsonInIsolate<T>(String jsonString, T Function(Map<String, dynamic>) fromJson) async {
  final map = await compute(_parseJson, jsonString);
  return fromJson(map);
}

// 顶层函数,供 compute 调用
Map<String, dynamic> _parseJson(String jsonString) {
  return jsonDecode(jsonString);
}

Isolate 的消息传递限制

Isolate 之间通过消息传递通信,但并非所有对象都能传递。

可以传递的类型

  • 基本类型:intdoubleboolStringnull
  • 集合:ListMapSet(元素也必须是可传递类型)
  • SendPort / ReceivePort
  • TransferableTypedData(高效传递二进制数据)
  • 自定义对象(仅限简单对象,见下方说明)

不能传递的类型

  • BuildContext —— 绑定到主 Isolate 的 Widget 树
  • Controller(如 AnimationControllerTextEditingController
  • Stream / StreamController
  • 包含闭包(函数)的对象
  • 包含不可传递类型字段的对象

传递自定义对象

dart
// ✅ 可以传递 —— 所有字段都是基本类型
class User {
  final String name;
  final int age;
  User({required this.name, required this.age});
}

// ❌ 不能传递 —— 包含不可传递的类型
class BadExample {
  final StreamController controller;  // 不可传递
  final void Function() callback;     // 闭包不可传递
}

注意

传递给 Isolate 的对象会被复制,而不是共享引用。子 Isolate 修改对象不会影响主 Isolate 中的原对象。

Isolate 线程池:Isolate.run

Dart 2.19 引入了 Isolate.run,它是 compute 的改进版本,使用系统管理的 Isolate 线程池,性能更好。

dart
import 'dart:isolate';

Future<void> main() async {
  final result = await Isolate.run(() {
    // 可以直接写闭包
    int sum = 0;
    for (int i = 0; i < 100000000; i++) {
      sum += i;
    }
    return sum;
  });
  print('结果: $result');
}

Isolate.run vs compute

特性Isolate.runcompute
Dart 版本>= 2.19Flutter 所有版本
闭包支持支持不支持
线程池系统管理每次新建 Isolate
性能更好(复用 Isolate)稍差(每次创建销毁)

推荐

如果你的项目 Dart SDK >= 2.19,优先使用 Isolate.run。它支持闭包,且使用线程池,性能更好。

完整实战示例:图片处理

dart
import 'dart:isolate';
import 'dart:ui';
import 'package:flutter/material.dart';

// 图片处理参数
class ImageProcessParam {
  final SendPort sendPort;
  final List<int> imageBytes;
  final double brightness;

  ImageProcessParam({
    required this.sendPort,
    required this.imageBytes,
    required this.brightness,
  });
}

// 子 Isolate:调整亮度
void adjustBrightness(ImageProcessParam param) {
  final codec = instantiateImageCodecFromBuffer(
    param.imageBytes.buffer.asUint8List().buffer as ImmutableBuffer,
  );
  // 简化示例:实际项目中使用 image 包处理
  final result = {'status': 'done', 'brightness': param.brightness};
  param.sendPort.send(result);
}

class ImageProcessPage extends StatefulWidget {
  const ImageProcessPage({super.key});

  @override
  State<ImageProcessPage> createState() => _ImageProcessPageState();
}

class _ImageProcessPageState extends State<ImageProcessPage> {
  double _brightness = 1.0;
  bool _isProcessing = false;

  Future<void> _processImage() async {
    setState(() => _isProcessing = true);

    final receivePort = ReceivePort();
    await Isolate.spawn(
      adjustBrightness,
      ImageProcessParam(
        sendPort: receivePort.sendPort,
        imageBytes: [], // 实际传入图片字节
        brightness: _brightness,
      ),
    );

    receivePort.listen((message) {
      setState(() => _isProcessing = false);
      receivePort.close();
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('处理完成')),
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Isolate 图片处理')),
      body: Column(
        children: [
          Text('亮度: ${_brightness.toStringAsFixed(1)}'),
          Slider(
            value: _brightness,
            min: 0.0,
            max: 2.0,
            onChanged: _isProcessing ? null : (v) => setState(() => _brightness = v),
          ),
          ElevatedButton(
            onPressed: _isProcessing ? null : _processImage,
            child: _isProcessing
                ? const SizedBox(
                    width: 20,
                    height: 20,
                    child: CircularProgressIndicator(strokeWidth: 2),
                  )
                : const Text('处理图片'),
          ),
        ],
      ),
    );
  }
}

常见问题

1. Isolate 和 async/await 有什么区别?

特性async/awaitIsolate
本质单线程内的异步调度多线程并发
CPU 占用等待时不占 CPU独占一个 CPU 核心
适用场景I/O 操作(网络、文件)CPU 密集型计算
UI 影响不阻塞 UI不阻塞 UI
dart
// async/await —— 适合 I/O 操作
Future<void> fetchData() async {
  final response = await http.get(Uri.parse('https://api.example.com/data'));
  // 等待网络时,CPU 是空闲的,其他任务可以执行
}

// Isolate —— 适合 CPU 密集型操作
Future<void> calculate() async {
  final result = await compute(heavyWork, data);
  // 计算在另一个线程,主线程继续处理 UI
}

2. 为什么 compute 的函数不能是实例方法?

compute 要求传入的函数必须是顶层函数静态方法,因为实例方法隐式绑定了 this,而 this 指向的对象无法跨 Isolate 传递。

dart
// ❌ 错误:实例方法
class MyWidget extends StatefulWidget {
  int calculate(int n) { ... }  // 隐式包含 this
}

// 在 compute 中使用
compute(this.calculate, 100);  // 报错!

// ✅ 正确:顶层函数
int calculate(int n) {
  int sum = 0;
  for (int i = 0; i < n; i++) sum += i;
  return sum;
}

compute(calculate, 100);  // 正常

3. 如何取消正在运行的 Isolate?

dart
Future<void> runCancellableTask() async {
  final receivePort = ReceivePort();

  // spawn 返回 Isolate 对象
  final isolate = await Isolate.spawn(
    longRunningTask,
    receivePort.sendPort,
  );

  // 设置超时自动取消
  bool completed = false;

  receivePort.listen((message) {
    completed = true;
    receivePort.close();
  });

  // 3 秒后如果没完成就杀掉 Isolate
  await Future.delayed(const Duration(seconds: 3));
  if (!completed) {
    isolate.kill(priority: Isolate.immediate);
    receivePort.close();
    print('任务超时,已取消');
  }
}

方案选择指南

场景推荐方案
一次性耗时计算computeIsolate.run
JSON / 数据解析compute
图片 / 音视频处理Isolate.spawn + 双向通信
需要进度回调的长时间任务Isolate.spawn + SendPort
需要取消任务Isolate.spawn + isolate.kill()
I/O 操作(网络、文件读写)async/await(不需要 Isolate)

核心原则

  1. I/O 操作用 async/await —— 不占 CPU,不需要 Isolate
  2. CPU 密集型任务用 Isolate —— 避免 UI 卡顿
  3. 简单场景用 compute —— 代码最少,自动管理
  4. 复杂场景用 Isolate.spawn —— 双向通信、任务取消等

下一步

基于 Flutter 官方文档整理