Dart: Dart Isolates
Última atualização: 2026-08-26
Isolate é a forma do Dart fazer paralelismo — sem memória compartilhada, naturalmente livre de race conditions, seguro e eficiente.
1. O que Você Aprenderá
- Isolate vs Thread: Um modelo de concorrência sem memória compartilhada
- Isolate.spawn com ReceivePort / SendPort para comunicação bidirecional
- Isolate.run: Uma API simplificada para tarefas únicas
- A função auxiliar Compute e o compute do Flutter
- Cenário do Bob: DataPipeline usando Isolates para processar milhões de pedidos em paralelo
2. A História Real de um Desenvolvedor
(1) A Dor: Processamento de Um Milhão de Registros Muito Lento em Uma Única Thread
O DataPipeline do Bob precisava realizar análises estatísticas sobre 1.200.000 pedidos. O processamento em thread única levava 15 segundos, mas os clientes SaaS exigiam que o relatório fosse gerado em até 5 segundos. Bob tentou multithreading, mas os bloqueios de memória compartilhada e as race conditions o levaram a gastar uma semana corrigindo bugs de concorrência antes de finalmente desistir. O relatório ainda levava 15 segundos.
(2) A Solução com Isolates
Os Isolates do Dart são unidades paralelas sem memória compartilhada. Cada Isolate possui sua própria memória heap independente e se comunica via passagem de mensagens. Sem memória compartilhada significa sem race conditions.
// Dividir 1,2M pedidos em 4 Isolates, cada um processa 300K
final results = await Future.wait([
Isolate.run(() => processChunk(orders.sublist(0, 300000))),
Isolate.run(() => processChunk(orders.sublist(300000, 600000))),
Isolate.run(() => processChunk(orders.sublist(600000, 900000))),
Isolate.run(() => processChunk(orders.sublist(900000, 1200000))),
]);
> **Saída:** Execute no DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
(3) Os Benefícios
- 4 Isolates rodando em paralelo reduziram o tempo de processamento de 15 segundos para 4 segundos
- Sem memória compartilhada = Sem race conditions = Sem locks = Sem bugs de concorrência
- O modelo de passagem de mensagens torna o código mais fácil de raciocinar
3. Fundamentos de Isolate
(1) Isolate vs Thread
flowchart TD
A[Main Isolate] -->|"SendPort"| B[Worker Isolate 1]
A -->|"SendPort"| C[Worker Isolate 2]
A -->|"SendPort"| D[Worker Isolate N]
B -->|"SendPort"| A
C -->|"SendPort"| A
D -->|"SendPort"| A
subgraph Data Sharding
B -- 300K pedidos
C -- 300K pedidos
D -- 400K pedidos
end
> **Saída:** Execute no DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
| Dimensão | Thread (Java/C++) | Isolate (Dart) |
|---|---|---|
| Memória | Compartilhada | Independente |
| Comunicação | Variáveis compartilhadas + Locks | Passagem de Mensagens |
| Race Conditions | Sim | Não |
| Sincronização de Dados | Bloqueio manual necessário | Nenhum lock necessário |
| Custo de Criação | Baixo | Médio (dados devem ser copiados) |
4. Isolate.run — Uma Tarefa Única Simplificada
(1) O Uso Mais Simples de Isolate
▶ Exemplo
> **Saída:** Execute no DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Básico do Isolate.run
import 'dart:isolate';
// Computação custosa para rodar em isolate
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
Future<void> main() async {
print('Computando fibonacci(40) em isolate...');
final result = await Isolate.run(() => fibonacci(40));
print('Resultado: $result');
}
> **Saída:** Execute no DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
▶ Exemplo
> **Saída:** Execute no DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Processamento de Pedaços de Dados com Isolate.run
import 'dart:isolate';
// Processar um pedaço de pedidos
double processChunk(List<Map<String, dynamic>> orders) {
double total = 0;
for (final order in orders) {
total += (order['amount'] as double);
}
return total;
}
Future<void> main() async {
// Simular 1M pedidos
final orders = List.generate(
1000000,
(i) => {'id': 'ORD-$i', 'amount': (i % 100 + 1) * 10.0},
);
// Dividir em 4 pedaços
final chunkSize = orders.length ~/ 4;
final chunks = List.generate(
4,
(i) => orders.sublist(i * chunkSize, (i + 1) * chunkSize),
);
// Processar pedaços em paralelo
final stopwatch = Stopwatch()..start();
final results = await Future.wait(
chunks.map((chunk) => Isolate.run(() => processChunk(chunk))),
);
stopwatch.stop();
final totalRevenue = results.fold(0.0, (a, b) => a + b);
print('Receita total: \$${totalRevenue.toStringAsFixed(2)} USD');
print('Tempo: ${stopwatch.elapsedMilliseconds}ms');
}
> **Saída:** Execute no DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
5. Isolate.spawn e Comunicação Bidirecional
(1) Comunicação SendPort / ReceivePort
▶ Exemplo
> **Saída:** Execute no DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Comunicação Bidirecional
import 'dart:isolate';
// Função worker - roda em isolate separado
void workerIsolate(SendPort mainSendPort) {
final workerReceivePort = ReceivePort();
// Enviar nossa porta de recebimento para o isolate principal
mainSendPort.send(workerReceivePort.sendPort);
// Ouvir mensagens do isolate principal
workerReceivePort.listen((message) {
if (message is String && message == 'shutdown') {
workerReceivePort.close();
return;
}
// Processar dados e enviar resultado de volta
if (message is List<double>) {
final total = message.fold(0.0, (a, b) => a + b);
mainSendPort.send(total);
}
});
}
Future<void> main() async {
final mainReceivePort = ReceivePort();
// Criar worker isolate
await Isolate.spawn(workerIsolate, mainReceivePort.sendPort);
// Obter porta de envio do worker
final workerSendPort = await mainReceivePort.first as SendPort;
// Criar uma nova porta de recebimento para resposta
final responsePort = ReceivePort();
workerSendPort.send([1500.0, 3200.0, 890.0]);
// Aguardar resposta
final result = await responsePort.first;
print('Resultado do worker: $result');
// Desligar worker
workerSendPort.send('shutdown');
}
> **Saída:** Execute no DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
▶ Exemplo
> **Saída:** Execute no DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Comunicação Multi-turno
import 'dart:isolate';
void dataWorker(SendPort mainPort) {
final receivePort = ReceivePort();
mainPort.send(receivePort.sendPort);
receivePort.listen((message) {
if (message == 'done') {
receivePort.close();
return;
}
if (message is Map<String, dynamic>) {
// Processar dados de pedido
final amount = message['amount'] as double;
final taxRate = message['taxRate'] as double? ?? 0.08;
final total = amount * (1 + taxRate);
mainPort.send({'id': message['id'], 'total': total});
}
});
}
Future<void> main() async {
final mainPort = ReceivePort();
await Isolate.spawn(dataWorker, mainPort.sendPort);
final workerPort = await mainPort.first as SendPort;
// Enviar múltiplas mensagens
final responsePort = ReceivePort();
workerPort.add({'id': 'ORD-001', 'amount': 1500.0, 'taxRate': 0.08});
workerPort.add({'id': 'ORD-002', 'amount': 3200.0});
// ... comunicação simplificada
workerPort.send('done');
}
> **Saída:** Execute no DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
6. Isolates e Transferência de Dados
(1) Regras de Transferência de Dados
| Método de Transferência | Descrição | Desempenho |
|---|---|---|
| Tipos Primitivos | int/double/String/bool | Cópia rápida |
| List/Map | Cópia profunda | Médio |
| Objetos Personalizados | Cópia profunda | Médio |
| SendPort | Passagem por referência | Rápido |
| Closures de Função | Passagem via Isolate.run | Verificado em tempo de compilação |
▶ Exemplo
> **Saída:** Execute no DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
: Melhor Prática para Passagem de Dados Grandes
import 'dart:isolate';
// Passar dados via closure (Isolate.run)
Future<double> processLargeData(List<double> amounts) async {
return Isolate.run(() {
// amounts é copiado para dentro do isolate
double sum = 0;
for (final a in amounts) {
sum += a;
}
return sum;
});
}
// Melhor: passar apenas o necessário
Future<double> processChunkOptimized(List<double> chunk) async {
return Isolate.run(() => chunk.fold(0.0, (a, b) => a + b));
}
void main() async {
final data = List.generate(1000000, (i) => (i + 1) * 1.0);
// Dividir e processar em paralelo
final chunkSize = data.length ~/ 4;
final futures = List.generate(4, (i) {
final chunk = data.sublist(i * chunkSize, (i + 1) * chunkSize);
return Isolate.run(() => chunk.fold(0.0, (a, b) => a + b));
});
final results = await Future.wait(futures);
final total = results.fold(0.0, (a, b) => a + b);
print('Total: $total');
}
> **Saída:** Execute no DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
7. Cenário do Bob: Processamento Paralelo de Um Milhão de Pedidos
▶ Exemplo
> **Saída:** Execute no DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
: Estatísticas Paralelas do DataPipeline
import 'dart:isolate';
class ChunkResult {
final int processed;
final int skipped;
final double revenue;
final Map<String, double> categoryRevenue;
ChunkResult({
required this.processed,
required this.skipped,
required this.revenue,
required this.categoryRevenue,
});
}
ChunkResult processChunk(List<Map<String, dynamic>> chunk) {
int processed = 0;
int skipped = 0;
double revenue = 0;
final categoryRevenue = <String, double>{};
for (final order in chunk) {
final amount = order['amount'] as double;
final status = order['status'] as String;
final category = order['category'] as String;
if (amount <= 0 || status == 'cancelled') {
skipped++;
continue;
}
processed++;
revenue += amount;
categoryRevenue.update(category, (v) => v + amount, ifAbsent: () => amount);
}
return ChunkResult(
processed: processed,
skipped: skipped,
revenue: revenue,
categoryRevenue: categoryRevenue,
);
}
Future<void> main() async {
// Gerar 1,2M pedidos
final categories = ['Electronics', 'Books', 'Clothing', 'Home', 'Sports'];
final statuses = ['completed', 'completed', 'completed', 'pending', 'cancelled'];
final orders = List.generate(1200000, (i) => {
'id': 'ORD-${i.toString().padLeft(6, '0')}',
'amount': (i % 500 + 10) * 1.0,
'status': statuses[i % statuses.length],
'category': categories[i % categories.length],
});
// Dividir em pedaços
final isolateCount = 4;
final chunkSize = orders.length ~/ isolateCount;
final chunks = List.generate(isolateCount, (i) {
final start = i * chunkSize;
final end = i == isolateCount - 1 ? orders.length : (i + 1) * chunkSize;
return orders.sublist(start, end);
});
// Processar em paralelo
final stopwatch = Stopwatch()..start();
final results = await Future.wait(
chunks.map((chunk) => Isolate.run(() => processChunk(chunk))),
);
stopwatch.stop();
// Agregar resultados
int totalProcessed = 0;
int totalSkipped = 0;
double totalRevenue = 0;
final totalCategoryRevenue = <String, double>{};
for (final r in results) {
totalProcessed += r.processed;
totalSkipped += r.skipped;
totalRevenue += r.revenue;
for (final entry in r.categoryRevenue.entries) {
totalCategoryRevenue.update(entry.key, (v) => v + entry.value, ifAbsent: () => entry.value);
}
}
print('=== Relatório Paralelo do DataPipeline ===');
print('Pedidos: ${orders.length}');
print('Processados: $totalProcessed');
print('Ignorados: $totalSkipped');
print('Receita: \$${totalRevenue.toStringAsFixed(2)} USD');
print('Tempo: ${stopwatch.elapsedMilliseconds}ms');
print('Isolates: $isolateCount');
print('\nPor Categoria:');
for (final entry in totalCategoryRevenue.entries.toList()..sort((a, b) => b.value.compareTo(a.value))) {
print(' ${entry.key}: \$${entry.value.toStringAsFixed(2)} USD');
}
}
> **Saída:** Execute no DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependente da versão do SDK.
8. Exemplo Completo: Framework de Processamento Paralelo com Isolates do DataPipeline
// ============================================
// Processamento Paralelo com Isolates do DataPipeline
// Framework para análise de dados em paralelo
// ============================================
import 'dart:isolate';
// Resultado do worker
class WorkerResult {
final int workerId;
final int processed;
final int skipped;
final double revenue;
final Map<String, double> categoryRevenue;
final Duration processingTime;
WorkerResult({
required this.workerId,
required this.processed,
required this.skipped,
required this.revenue,
required this.categoryRevenue,
required this.processingTime,
});
}
// Função worker - roda em isolate separado
WorkerResult processInIsolate((int, List<Map<String, dynamic>>) input) {
final (workerId, orders) = input;
final stopwatch = Stopwatch()..start();
int processed = 0;
int skipped = 0;
double revenue = 0;
final categoryRevenue = <String, double>{};
for (final order in orders) {
final amount = (order['amount'] as num).toDouble();
final status = order['status'] as String;
final category = order['category'] as String;
if (amount <= 0 || status == 'cancelled') {
skipped++;
continue;
}
processed++;
revenue += amount;
categoryRevenue.update(category, (v) => v + amount, ifAbsent: () => amount);
}
stopwatch.stop();
return WorkerResult(
workerId: workerId,
processed: processed,
skipped: skipped,
revenue: revenue,
categoryRevenue: categoryRevenue,
processingTime: stopwatch.elapsed,
);
}
// Gerenciador de pipeline paralelo
class ParallelPipeline {
final int isolateCount;
ParallelPipeline({this.isolateCount = 4});
Future<void> process(List<Map<String, dynamic>> orders) async {
print('=== Processamento Paralelo do DataPipeline ===');
print('Pedidos: ${orders.length}, Isolates: $isolateCount');
// Dividir pedidos em pedaços
final chunkSize = orders.length ~/ isolateCount;
final chunks = List.generate(isolateCount, (i) {
final start = i * chunkSize;
final end = i == isolateCount - 1 ? orders.length : (i + 1) * chunkSize;
return (i, orders.sublist(start, end));
});
// Processar em paralelo
final totalStopwatch = Stopwatch()..start();
final results = await Future.wait(
chunks.map((chunk) => Isolate.run(() => processInIsolate(chunk))),
);
totalStopwatch.stop();
// Agregar
int totalProcessed = 0;
int totalSkipped = 0;
double totalRevenue = 0;
final totalCategoryRevenue = <String, double>{};
print('\nResultados dos Workers:');
for (final r in results) {
totalProcessed += r.processed;
totalSkipped += r.skipped;
totalRevenue += r.revenue;
for (final entry in r.categoryRevenue.entries) {
totalCategoryRevenue.update(
entry.key, (v) => v + entry.value, ifAbsent: () => entry.value);
}
print(' Worker ${r.workerId}: ${r.processed} processados, '
'${r.processingTime.inMilliseconds}ms');
}
print('\n--- Relatório Agregado ---');
print('Processados: $totalProcessed pedidos');
print('Ignorados: $totalSkipped registros');
print('Receita: \$${totalRevenue.toStringAsFixed(2)} USD');
print('Tempo total: ${totalStopwatch.elapsedMilliseconds}ms');
print('\nPor Categoria:');
final sorted = totalCategoryRevenue.entries.toList()
..sort((a, b) => b.value.compareTo(a.value));
for (final entry in sorted) {
final pct = (entry.value / totalRevenue * 100).toStringAsFixed(1);
print(' ${entry.key}: \$${entry.value.toStringAsFixed(2)} USD ($pct%)');
}
}
}
void main() async {
// Gerar dados de exemplo
final categories = ['Electronics', 'Books', 'Clothing', 'Home', 'Sports'];
final statuses = ['completed', 'completed', 'completed', 'pending', 'cancelled'];
final orders = List.generate(500000, (i) => <String, dynamic>{
'id': 'ORD-${i.toString().padLeft(6, '0')}',
'amount': (i % 500 + 10) * 1.0,
'status': statuses[i % statuses.length],
'category': categories[i % categories.length],
});
final pipeline = ParallelPipeline(isolateCount: 4);
await pipeline.process(orders);
}
> **Saída:** Execute no DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
❓ Perguntas Frequentes
P: Qual a diferença entre um Isolate e uma Thread? R: Um Isolate possui sua própria memória heap independente e não compartilha dados; Threads compartilham a memória heap. Isolates se comunicam via passagem de mensagens, enquanto Threads se comunicam via variáveis compartilhadas + locks. Isolates não têm race conditions.
P: É custoso criar um Isolate? R: Criar um Isolate leva cerca de 50-150ms, o que é mais lento que criar uma thread. Criar e destruir frequentemente é ineficiente; é recomendado usar um Isolate Pool ou Isolate.run (que gerencia o ciclo de vida automaticamente).
P: Qual a diferença entre Isolate.run e Isolate.spawn? R: Isolate.run é uma API simplificada que executa uma tarefa única e depois fecha o Isolate automaticamente. Isolate.spawn cria um Isolate persistente, exigindo gerenciamento manual do ciclo de vida e comunicação.
P: Os dados são copiados ao serem transferidos entre Isolates? R: Sim. Todos os dados transferidos são copiados profundamente (exceto SendPort). Passar listas grandes tem custo de desempenho. Para dados muito grandes, considere transferir em pedaços.
P: Quantos tipos de Isolates o Dart possui? R: Principalmente dois: o Isolate de uso geral para paralelismo e o compute do Flutter para isolamento de UI. A plataforma web não suporta Isolates reais e usa Web Workers como simulação.
P: Existe um limite superior para o número de Isolates? R: Não há limite rígido, mas cada Isolate ocupa cerca de 2MB de memória. Na prática, é recomendado não exceder o número de núcleos da CPU para evitar troca de contexto excessiva.
P: Para quais cenários os Isolates são adequados? R: Computações intensivas de CPU (análise de dados, processamento de imagens, cálculos criptográficos). Para tarefas intensivas de I/O, Future/Stream é suficiente e Isolates não são necessários.
📖 Resumo
- Isolates são as unidades paralelas do Dart com memória independente, comunicação por passagem de mensagens e naturalmente livres de race conditions
- Isolate.run é adequado para tarefas de computação únicas e gerencia o ciclo de vida automaticamente
- Isolate.spawn + SendPort/ReceivePort é adequado para comunicação persistente
- Transferências de dados envolvem cópia; para dados grandes, transfira em pedaços
- 4 Isolates processando um milhão de pedidos em paralelo reduzem o tempo de 15 segundos para 4 segundos
📝 Exercícios
- Básico (⭐): Use
Isolate.runpara calcular o 42º número de Fibonacci e compare o tempo com o cálculo síncrono na thread principal. - Intermediário (⭐⭐): Gere 1.000.000 de números aleatórios, divida-os em 4 partes e use 4 Isolates para calcular a soma e a média de cada parte em paralelo. Finalmente, agregue os resultados no Isolate principal.
- Desafio (⭐⭐⭐): Implemente um Isolate Pool usando
Isolate.spawn: Pré-crie N Worker Isolates, faça o Isolate principal distribuir tarefas via SendPort, e faça os Workers retornarem resultados após o processamento. Suporte fila de tarefas e balanceamento de carga.