Orientação gerada a partir de informações públicas. Apenas para referência.
O pacote Actors é uma biblioteca Dart que simplifica o uso de Isolates ao implementar o Modelo de Ator. Ele fornece uma interface leve e intuitiva para criar e gerenciar contextos de execução isolados, permitindo programação concorrente segura sem estado compartilhado. Ao abstrair a complexidade dos Isolates brutos, permite que os desenvolvedores enviem mensagens entre atores de forma assíncrona, tornando-o ideal para construir aplicações responsivas e escaláveis. A biblioteca é particularmente útil em cenários que exigem alta concorrência com risco mínimo de condições de corrida.
Casos de uso
Criando interfaces responsivas com processamento em segundo plano
Implementando concorrência por troca de mensagens
Gerenciando tarefas de longa duração sem bloquear a thread principal
Criando componentes modulares e isolados
Gerenciando estado em múltiplos processos independentes
Principais recursos
Criação simples de ator com a classe `Actor`
Passagem de mensagem por meio de `send` e `receive`
Tratamento automático de erros e isolamento
Suporte para comunicação síncrona e assíncrona
Invólucro leve em torno da API do Isolate do Dart
Indicado para
Desenvolvedores procurando concorrência segura em aplicativos Dart
Projetos que exigem operações isoladas e não bloqueantes
Aplicações que precisam de componentes modulares e compostos
Equipes adotando o modelo de ator para concorrência
Aplicativos Flutter com lógica de fundo complexa
Considerações
Requer entendimento do conceito de Modelo de Ator
Limitado à concorrência baseada em Isolates do Dart
Não adequado para cenários de memória compartilhada
Sobrecarga de desempenho da serialização de mensagens
Melhor usado quando os isolados são a ferramenta certa para a tarefa (não para tarefas assíncronas simples)
README original
Snapshot do README original do repositório-fonte vinculado. Consulte o repositório para o conteúdo mais recente.
actors
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:
If your actor does not maintain internal state, it can also be created from a function, or even a lambda:
inttwo(int n) => n * 2;
main() async {
final actor = Actor.of(two);
print(await actor.send(5)); // 10await 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.classStreamGeneratorwithHandler<int, Stream<int>> {
@overrideStream<int> handle(int message) {
returnStream.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);
awaitfor (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:
classHttpServerActorwithHandler<Message, Answer> {
latefinalHttpServer _server;
finalint port;
HttpServerActor(this.port);
// this method will only run in the Actor's own Isolate, so we can// create non-sendable state.@overrideFuture<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 = awaitHttpServer.bind(InternetAddress.loopbackIPv4, port, shared: true);
unawaited(_serveRequests());
}
// ...
}
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 mActors, 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 actorsfinal 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)); // 8print(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.