Flutter并发执行插件simple_isolate的使用

Flutter并发执行插件simple_isolate的使用

simple_isolate 是一个简化版的 Dart 隔离(isolate)插件。它提供了基于 Future 的 API,支持返回数据、完成处理以及异常处理等功能。

特性

  • 基于 Future 的 API,支持返回数据、完成处理以及异常处理。
  • 支持双向通信,可以在隔离(isolate)和外部世界之间发送消息。

使用方法

基本用法

首先,安装并导入此包:

import 'package:simple_isolate/simple_isolate.dart';

使用 SimpleIsolate.spawn<T>(entrypoint, argument) 替代 Isolate.spawn 来运行一个类型为 Future<T> Function(SIContext ctx) 的函数作为隔离入口点。

  • SIContext 类型可以在许多情况下使用,我们将在下面的例子中详细介绍。
  • SIContext.argument 可以获取传递给入口点函数的参数。
  • 注意入口点函数返回一个 Future<T>,你可以在数据可跨隔离边界序列化时返回数据到调用方。
  • SimpleIsolate.spawn 是对 Isolate.spawn 的封装,返回一个 Future<SimpleIsolate<T>>
  • SimpleIsolate.future 用于等待完成或处理来自入口点函数的异常。
  • SimpleIsolate.core 返回内部的 Dart [Isolate]。

例如,返回一些数据:

var si = await SimpleIsolate.spawn<String>(
  (SIContext ctx) async {
    var result = '';
    // 使用 `ctx.argument` 获取传递给入口点函数的参数。
    var to = ctx.argument as int;
    for (var i = 0; i < to; i++) {
      result += '<data chunk $i>';
      await Future<void>.delayed(Duration(milliseconds: 500));
    }
    return result;
  },
  3,
);

// 等待隔离函数完成。
// 并打印结果。
print(await si.future);
/**
  * <data chunk 0><data chunk 1><data chunk 2>
  */

异常处理

由于它是基于 Future 的 API,你可以简单地在 await 语句中使用 try-catch 块来处理异常:

var si = await SimpleIsolate.spawn<String>(
  (SIContext ctx) async {
    await Future<void>.delayed(Duration(milliseconds: 500));
    throw Exception('Oops!');
  },
  3,
);
try {
  print(await si.future);
} catch (err) {
  print('ERROR: $err');
}
/**
  * ERROR: Exception: Oops!
  */

从隔离发送消息

使用 Context.sendMsg 从隔离向外部发送消息。simple_isolate 中的消息定义如下:

class SIMsg {
  final String name;
  final Map<String, dynamic>? params;
  SIMsg(this.name, this.params);
}

要处理从隔离发送的消息,可以使用 SimpleIsolate.spawn 中的 onMsgReceived 参数:

Future<void> sendMessagesFromIsolate() async {
  var si = await SimpleIsolate.spawn<String>(
    (SIContext ctx) async {
      var result = '';
      var to = ctx.argument as int;
      for (var i = 0; i < to; i++) {
        result += '<data chunk $i>';
        ctx.sendMsg(
            'got-data', {'index': i, 'currentResult': result});
        await Future<void>.delayed(Duration(milliseconds: 500));
      }
      return result;
    },
    3,
    onMsgReceived: (msg) {
      switch (msg.name) {
        case 'got-data':
          {
            print('MSG> ${msg.params}');
            break;
          }

        default:
          {
            print(
                'Unsupported message ${msg.name}, something went wrong in your code.');
            break;
          }
      }
    },
  );
  print(await si.future);
  /**
   * MSG> {index: 0, currentResult: <data chunk 0>}
   * MSG> {index: 1, currentResult: <data chunk 0><data chunk 1>}
   * MSG> {index: 2, currentResult: <data chunk 0><data chunk 1><data chunk 2>}
   * <data chunk 0><data chunk 1><data chunk 2>
   */
}

向隔离发送消息

调用 SimpleIsolate.spawn 并设置 bidirectional: true 以启用双向通信。

要向隔离发送消息,使用 SimpleIsolate.sendMsgToIsolate。并在隔离内使用 SIContext.onMsgReceivedInIsolate 处理这些消息。

注意,如果你的入口点函数退出得太早,发送到隔离的消息可能不会被处理。消息处理依赖于隔离内的内部事件循环。如果退出得太早,隔离会立即退出而无法处理 onMsgReceivedInIsolate。在下面的示例中,我们调用了 Future<void>.delayed 来利用与隔离关联的事件循环。

Future<void> sendMessagesToIsolate() async {
  // 从类型为 [Future<String>(int)] 的函数创建 [SimpleIsolate]。
  var si = await SimpleIsolate.spawn<String>(
    (SIContext ctx) async {
      var result = '';
      ctx.onMsgReceivedInIsolate = (msg) {
        switch (msg.name) {
          case 'inject':
            {
              result += msg.params?['value'] as String;
              break;
            }

          default:
            {
              print(
                  'Unsupported message ${msg.name}, something went wrong in your code.');
              break;
            }
        }
      };
      var to = ctx.argument as int;
      for (var i = 0; i < to; i++) {
        result += '<data chunk $i>';
        await Future<void>.delayed(Duration(milliseconds: 500));
      }
      return result;
    },
    3,
  );
  await si
      .sendMsgToIsolate('inject', {'value': '<injected!!!>'});
  print(await si.future);
  /**
   * <data chunk 0><injected!!!><data chunk 1><data chunk 2>
   */
}

当隔离被杀死时收到通知

当隔离被杀死时,SimpleIsolate.future 将抛出 SimpleIsolateAbortException 异常。

var si = await SimpleIsolate.spawn<String>(
  (SIContext ctx) async {
    var count = ctx.argument as int;
    var result = '';
    for (var i = 0; i < count; i++) {
      var data = '<data chunk $i>';
      print('--> Appending data $data');
      result += data;
      await Future<void>.delayed(Duration(milliseconds: 500));
    }
    return result;
  },
  4,
);

try {
  await Future.wait<String>([
    // 等待隔离完成。
    si.future,
    // 在 1 秒后杀死隔离。
    () async {
      await Future<void>.delayed(Duration(seconds: 1));
      si.core.kill();
      return Future.value('');
    }(),
  ]);
} on SimpleIsolateAbortException catch (_) {
  print('Isolation killed');
}

完整示例代码

以下是一个完整的示例代码,展示了如何使用 simple_isolate 插件:

import 'package:simple_isolate/simple_isolate.dart';

Future<void> futureCompletion() async {
  final si = await SimpleIsolate.spawn<String>(
    (SIContext ctx) async {
      final count = ctx.argument as int;
      var result = '';
      for (var i = 0; i < count; i++) {
        result += '<data chunk $i>';
        await Future<void>.delayed(Duration(milliseconds: 500));
      }
      return result;
    },
    3,
  );
  print(await si.future);
  /**
   * <data chunk 0><data chunk 1><data chunk 2>
   */
}

Future<void> futureException() async {
  final si = await SimpleIsolate.spawn<String>(
    (SIContext ctx) async {
      await Future<void>.delayed(Duration(milliseconds: 500));
      throw Exception('Oops!');
    },
    3,
  );
  try {
    print(await si.future);
  } catch (err) {
    print('ERROR: $err');
  }
  /**
   * ERROR: Exception: Oops!
   */
}

Future<void> sendMessagesFromIsolate() async {
  final si = await SimpleIsolate.spawn<String>(
    (SIContext ctx) async {
      var result = '';
      final to = ctx.argument as int;
      for (var i = 0; i < to; i++) {
        result += '<data chunk $i>';
        ctx.sendMsg(
            'got-data', {'index': i, 'currentResult': result});
        await Future<void>.delayed(Duration(milliseconds: 500));
      }
      return result;
    },
    3,
    onMsgReceived: (msg) {
      switch (msg.name) {
        case 'got-data':
          {
            print('MSG> ${msg.params}');
            break;
          }

        default:
          {
            print(
                'Unsupported message ${msg.name}, something went wrong in your code.');
            break;
          }
      }
    },
  );
  print(await si.future);
  /**
   * MSG> {index: 0, currentResult: <data chunk 0>}
   * MSG> {index: 1, currentResult: <data chunk 0><data chunk 1>}
   * MSG> {index: 2, currentResult: <data chunk 0><data chunk 1><data chunk 2>}
   * <data chunk 0><data chunk 1><data chunk 2>
   */
}

Future<void> sendMessagesToIsolate() async {
  final si = await SimpleIsolate.spawn<String>((SIContext ctx) async {
    var result = '';
    ctx.onMsgReceivedInIsolate = (msg) {
      switch (msg.name) {
        case 'inject':
          {
            result += msg.params?['value'] as String;
            break;
          }

        default:
          {
            print(
                'Unsupported message ${msg.name}, something went wrong in your code.');
            break;
          }
      }
    };
    final to = ctx.argument as int;
    for (var i = 0; i < to; i++) {
      result += '<data chunk $i>';
      await Future<void>.delayed(Duration(milliseconds: 500));
    }
    return result;
  }, 3, bidirectional: true);
  await si
      .sendMsgToIsolate('inject', {'value': '<injected!!!>'});
  print(await si.future);
  /**
   * <data chunk 0><injected!!!><data chunk 1><data chunk 2>
   */
}

Future<void> kill() async {
  final si = await SimpleIsolate.spawn<String>(
    (SIContext ctx) async {
      final count = ctx.argument as int;
      var result = '';
      for (var i = 0; i < count; i++) {
        final data = '<data chunk $i>';
        print('--> Appending data $data');
        result += data;
        await Future<void>.delayed(Duration(milliseconds: 500));
      }
      return result;
    },
    4,
  );

  try {
    await Future.wait<String>([
      si.future,
      () async {
        await Future<void>.delayed(Duration(seconds: 1));
        si.kill();
        return Future.value('');
      }(),
    ]);
  } on SimpleIsolateAbortException catch (_) {
    print('Isolation killed');
  }
}

Future<void> onSpawn() async {
  final si = await SimpleIsolate.spawn<String>(
    (SIContext ctx) async {
      final count = ctx.argument as int;
      var result = '';
      for (var i = 0; i < count; i++) {
        result += '<data chunk $i>';
        await Future<void>.delayed(Duration(milliseconds: 500));
      }
      return result;
    },
    3,
    onSpawn: (dynamic argument) => print('onSpawn called with $argument'),
  );
  print(await si.future);
  /**
   * <data chunk 0><data chunk 1><data chunk 2>
   */
}

void main(List<String> args) async {
  await futureCompletion();
  try {
    await futureException();
  } catch (err) {
    print('Error: $err');
  }
  await sendMessagesFromIsolate();
  await sendMessagesToIsolate();
  await kill();
  await onSpawn();
}

更多关于Flutter并发执行插件simple_isolate的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter并发执行插件simple_isolate的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,simple_isolate 是一个用于简化并发执行的插件,它允许你在不同的隔离(Isolation)中执行任务,从而提高应用程序的性能。Isolates 是Dart中的并发模型,它们允许代码在独立的线程中运行,从而实现并行处理。

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 simple_isolate 的依赖:

dependencies:
  flutter:
    sdk: flutter
  simple_isolate: ^0.1.0

然后运行 flutter pub get 来安装依赖。

2. 使用 simple_isolate

simple_isolate 提供了一个简单的API来在隔离中执行任务。以下是一个基本的使用示例:

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

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: IsolateExample(),
    );
  }
}

class IsolateExample extends StatefulWidget {
  [@override](/user/override)
  _IsolateExampleState createState() => _IsolateExampleState();
}

class _IsolateExampleState extends State<IsolateExample> {
  String _result = 'Waiting for result...';

  Future<void> _runTaskInIsolate() async {
    // 使用 SimpleIsolate 在隔离中执行任务
    final result = await SimpleIsolate.run(_heavyComputation, 10);
    setState(() {
      _result = 'Result: $result';
    });
  }

  static int _heavyComputation(int value) {
    // 模拟一个耗时的计算任务
    return value * 2;
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Simple Isolate Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(_result),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _runTaskInIsolate,
              child: Text('Run Task in Isolate'),
            ),
          ],
        ),
      ),
    );
  }
}
回到顶部