搜索插件、代码仓与 APP 分类

插件详情v0.11.0

actors

Dart 的 Actors 模型库。它是 Dart 的 `Isolate` 的轻量级封装,使其更易于使用。

AndroidiOSmacOSWindowsLinux
近 30 天下载
1820
喜欢数
29
Pub 分数
160

安装插件

flutter pub add actors

插件介绍

基于插件公开信息、社区数据与项目使用情况整理的选型参考。

Actors 包是一个 Dart 库,通过实现 Actor 模型简化了 Isolates 的使用。它提供了一个轻量级、直观的接口,用于创建和管理隔离的执行上下文,从而在无共享状态的情况下实现安全的并发编程。通过抽象掉原始 Isolates 的复杂性,该库允许开发者异步地在各个参与者之间发送消息,非常适合构建响应迅速且可扩展的应用程序。在需要高并发且最小化竞态条件风险的场景中,该库尤为有用。

适合场景

  • 使用后台处理构建响应式 UI
  • 实现消息传递并发
  • 在不阻塞主线程的情况下处理长时间运行的任务
  • 创建模块化、隔离的组件
  • 跨多个独立进程管理状态

主要特点

  • 使用 `Actor` 类进行简单的演员创建
  • 通过 `send` 和 `receive` 进行消息传递
  • 自动错误处理和隔离
  • 支持同步和异步通信
  • Dart Isolate API 的轻量级封装

适合对象

  • 寻求在 Dart 应用中实现安全并发的开发者
  • 需要隔离的、非阻塞操作的项目
  • 需要模块化、可组合组件的应用程序
  • 采用演员模型进行并发的团队
  • 具有复杂后台逻辑的 Flutter 应用

使用建议

  • 需要理解Actor模型的概念
  • 受限于 Dart 的 Isolate 基于的并发
  • 不适用于共享内存场景
  • 消息序列化带来的性能开销
  • 在分离的实例是合适工具时使用效果最佳(不适用于简单的异步任务)

原始 README

以下为关联源码仓库的 README 原文快照,最新内容请访问源码仓库。

actors

Actors CI pub package

actors is a library that enables the use of the Actors Model in Dart.

It is a thin wrapper around Dart's Isolate (on Flutter and Dart VM) and Web Workers (on the Web - TODO) that makes them much easier to use.

Actor

To start an Actor is very easy. You create a Handler implementing the logic to handle messages within the Actor's Isolate, then create an Actor using it:

class Accumulator with Handler<int, int> {
  int _value;
  
  Accumulator([int initialValue = 0]): _value = initialValue;
  
  int handle(int n) => _value += n;
}

main() async {
  final actor = Actor.create(() => Accumulator(6));
  print(await actor.send(5)); // 11
  await actor.close();
}

If your actor does not maintain internal state, it can also be created from a function, or even a lambda:

int two(int n) => n * 2;

main() async {
  final actor = Actor.of(two);
  print(await actor.send(5)); // 10
  await actor.close();
}

As you can see, an Actor can send a message back to the caller asynchronously.

They can also send more than one message back by returning a Stream:

// A Handler that returns a Stream must use a StreamActor, not an Actor.
class StreamGenerator with Handler<int, Stream<int>> {
  @override
  Stream<int> handle(int message) {
    return Stream.fromIterable(Iterable.generate(message, (i) => i));
  }
}

main() async {
  // Create an StreamActor from a Handler that returns Stream.
  final actor = StreamActor.create(StreamGenerator.new);
  final stream = actor.send(2);
  await for (final item in stream) {
    print(item); // 0, 1
  }
  await actor.close();
}
Actor state

An actor can safely maintain internal state which cannot be reached by any other actors (as it resides in its own Dart Isolate in the DartVM - please notice that this does not hold on the web).

The actor's state can include anything, even Streams and open sockets, for example, except if the Handler is instantiated locally by using the Actor(Handler handler) constructor. For this reason, prefer to use either the Actor.of constructors to wrap functions, or the Actor.create(Handler Function()) constructor that ensures the Handler is not instantiated locally.

Initial state that requires asynchronous calls can be initialized in the Handler's init method. actors guarantees that an Actor will never be called to handle a message before its init method has successfully returned.

For example, an Actor which wraps a HttpServer could be initialized as shown below:

class HttpServerActor with Handler<Message, Answer> {
  late final HttpServer _server;
  final int port;

  HttpServerActor(this.port);

  // this method will only run in the Actor's own Isolate, so we can
  // create non-sendable state.
  @override
  Future<void> init() async {
    // binding a server with "shared: true" means that requests will be handled
    // by multiple Isolates if HttpServerActor is started on many Isolates (see ActorGroup). 
    _server = await HttpServer.bind(InternetAddress.loopbackIPv4, port, shared: true);
    unawaited(_serveRequests());
  }
  
  // ...
}

You can see the full example code at example/stateful_actor_example.dart.

Sending an Actor to another Actor

To send an Actor to another Actor is not possible directly, but you can send its Sendable object, which can be obtained by calling toSendable() (unfortunately, this does not currently work for StreamActor).

See the inter_actor_test test for an example where an Actor's sender function is given to another Actor via its constructor.

This enables a common pattern where many actors are given a reference to another actor which can aggregate their results in one place.

Notice that using a Sendable to send messages has a small performance overhead when compared to a plain Actor. For the big_messages benchmark, Actor messages' round trip is measured at ~21us VS 28us for Sendable (using a Macbook Air from 2019).

Actors themselves perform identically to using plain Isolates directly.

ActorGroup

ActorGroup allows several Actor instances to be grouped together, all based on the same Handler implementation, but executed according to one of the available strategies:

  • RoundRobin - send message to a single Actor, alternating which member of the group receives the message.
  • MultiHandler - send message to m Actors, wait for at least n successful answers.

RoundRobing is appropriate for cases where messages are CPU intensive to handle and there may be many of them.

MultiHandler is a way to achieve high reliability by duplicating effort, as not all Actors in the group may be healthy at all times. Having a few "backups" doing the same work on each message may be a good idea in case one or more of the expected receivers are likely to fail, as the system will still continue to work without issues as long as n actors remain healthy... Also, by sending the same message to several actors, the message might be received in different locations, making it much harder for it to be lost.

// create a group of 4 actors
final group = ActorGroup(Two(), size: 4);
print(await group.send(5)); // 10
group.close();

Messenger

The Messenger mixin is implemented by Actor, ActorGroup, and also LocalMessenger, which runs its Handler in the local Isolate.

Messenger<int, int> messenger;

// a Messenger can be local
messenger = LocalMessenger(Two());
print(await messenger.send(2)); // 4

// or it can be an Actor
messenger = Actor(Two());
print(await messenger.send(3)); // 6
messenger.close();

// or an ActorGroup
messenger = ActorGroup(Two(), size: 2);
print(await messenger.send(4)); // 8
print(await messenger.send(5)); // 10
messenger.close();

This makes it possible to write code that works the same whether the message is handled locally or in another Isolate.

More examples

所属分类

源码仓库

使用此插件的项目

暂无相关数据。