Dart: Dart Isolates

Last updated: 2026-08-26

Isolate is Dart's way of doing parallelism — no shared memory, naturally race-free, safe, and efficient.

1. What You'll Learn


2. A Developer's Real Story

(1) The Pain: Processing a Million Records Too Slowly in a Single Thread

Bob's DataPipeline needed to perform statistical analysis on 1,200,000 orders. Single-threaded processing took 15 seconds, but SaaS clients required the report to be generated within 5 seconds. Bob tried multithreading, but the shared memory locks and race conditions led him to spend a week fixing concurrency bugs before finally giving up. The report still took 15 seconds.

(2) The Isolate Solution

Dart's Isolates are parallel units without shared memory. Each Isolate has its own independent heap memory and communicates via message passing. No shared memory means no race conditions.

DART
// Split 1.2M orders into 4 Isolates, each processes 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))),
]);
TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly with different SDK versions.

(3) The Benefits


3. Isolate Fundamentals

(1) Isolate vs Thread

100%
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 orders
    C -- 300K orders
    D -- 400K orders
  end
TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly with different SDK versions.
Dimension Thread (Java/C++) Isolate (Dart)
Memory Shared Independent
Communication Shared variables + Locks Message Passing
Race Conditions Yes No
Data Synchronization Manual locking required No locks needed
Creation Overhead Low Medium (data must be copied)

4. Isolate.run — A Simplified One-Shot Task

(1) The Simplest Isolate Usage

▶ Example

TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly with different SDK versions.

: Isolate.run Basics

DART
import 'dart:isolate';

// Expensive computation to run in isolate
int fibonacci(int n) {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

Future<void> main() async {
  print('Computing fibonacci(40) in isolate...');

  final result = await Isolate.run(() => fibonacci(40));

  print('Result: $result');
}
TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly with different SDK versions.

▶ Example

TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly with different SDK versions.

: Processing Data Chunks with Isolate.run

DART
import 'dart:isolate';

// Process a chunk of orders
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 {
  // Simulate 1M orders
  final orders = List.generate(
    1000000,
    (i) => {'id': 'ORD-$i', 'amount': (i % 100 + 1) * 10.0},
  );

  // Split into 4 chunks
  final chunkSize = orders.length ~/ 4;
  final chunks = List.generate(
    4,
    (i) => orders.sublist(i * chunkSize, (i + 1) * chunkSize),
  );

  // Process chunks in parallel
  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('Total revenue: \$${totalRevenue.toStringAsFixed(2)} USD');
  print('Time: ${stopwatch.elapsedMilliseconds}ms');
}
TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly with different SDK versions.

5. Isolate.spawn and Bidirectional Communication

(1) SendPort / ReceivePort Communication

▶ Example

TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly with different SDK versions.

: Bidirectional Communication

DART
import 'dart:isolate';

// Worker function - runs in separate isolate
void workerIsolate(SendPort mainSendPort) {
  final workerReceivePort = ReceivePort();

  // Send our receive port to main isolate
  mainSendPort.send(workerReceivePort.sendPort);

  // Listen for messages from main isolate
  workerReceivePort.listen((message) {
    if (message is String && message == 'shutdown') {
      workerReceivePort.close();
      return;
    }

    // Process data and send result back
    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();

  // Spawn worker isolate
  await Isolate.spawn(workerIsolate, mainReceivePort.sendPort);

  // Get worker's send port
  final workerSendPort = await mainReceivePort.first as SendPort;

  // Create a new receive port for response
  final responsePort = ReceivePort();
  workerSendPort.send([1500.0, 3200.0, 890.0]);

  // Wait for response
  final result = await responsePort.first;
  print('Result from worker: $result');

  // Shutdown worker
  workerSendPort.send('shutdown');
}
TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly with different SDK versions.

▶ Example

TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly with different SDK versions.

: Multi-round Communication

DART
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>) {
      // Process order data
      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;

  // Send multiple messages
  final responsePort = ReceivePort();
  workerPort.add({'id': 'ORD-001', 'amount': 1500.0, 'taxRate': 0.08});
  workerPort.add({'id': 'ORD-002', 'amount': 3200.0});

  // ... simplified communication
  workerPort.send('done');
}
TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly with different SDK versions.

6. Isolates and Data Transfer

(1) Data Transfer Rules

Transfer Method Description Performance
Primitive Types int/double/String/bool Fast copy
List/Map Deep copy Medium
Custom Objects Deep copy Medium
SendPort Passed by reference Fast
Function Closures Passed via Isolate.run Checked at compile time
⚠️ Note: Data passed between Isolates is copied, not shared. Passing large lists incurs a copying cost. For large data, consider transferring in chunks or using closures with Isolate.run.

▶ Example

TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly with different SDK versions.

: Best Practice for Passing Large Data

DART
import 'dart:isolate';

// Pass data via closure (Isolate.run)
Future<double> processLargeData(List<double> amounts) async {
  return Isolate.run(() {
    // amounts is copied into the isolate
    double sum = 0;
    for (final a in amounts) {
      sum += a;
    }
    return sum;
  });
}

// Better: pass only what's needed
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);

  // Split and process in parallel
  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');
}
TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly with different SDK versions.

7. Bob's Scenario: Parallel Processing of a Million Orders

▶ Example

TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly with different SDK versions.

: DataPipeline Parallel Statistics

DART
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 {
  // Generate 1.2M orders
  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],
  });

  // Split into chunks
  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);
  });

  // Process in parallel
  final stopwatch = Stopwatch()..start();
  final results = await Future.wait(
    chunks.map((chunk) => Isolate.run(() => processChunk(chunk))),
  );
  stopwatch.stop();

  // Aggregate results
  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('=== DataPipeline Parallel Report ===');
  print('Orders:    ${orders.length}');
  print('Processed: $totalProcessed');
  print('Skipped:   $totalSkipped');
  print('Revenue:   \$${totalRevenue.toStringAsFixed(2)} USD');
  print('Time:      ${stopwatch.elapsedMilliseconds}ms');
  print('Isolates:  $isolateCount');

  print('\nBy Category:');
  for (final entry in totalCategoryRevenue.entries.toList()..sort((a, b) => b.value.compareTo(a.value))) {
    print('  ${entry.key}: \$${entry.value.toStringAsFixed(2)} USD');
  }
}
TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly with different SDK versions.

8. Complete Example: DataPipeline Isolate Parallel Processing Framework

DART
// ============================================
// DataPipeline Isolate Parallel Processing
// Framework for parallel data analytics
// ============================================

import 'dart:isolate';

// Worker result
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,
  });
}

// Worker function - runs in separate isolate
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,
  );
}

// Parallel pipeline manager
class ParallelPipeline {
  final int isolateCount;

  ParallelPipeline({this.isolateCount = 4});

  Future<void> process(List<Map<String, dynamic>> orders) async {
    print('=== DataPipeline Parallel Processing ===');
    print('Orders: ${orders.length}, Isolates: $isolateCount');

    // Split orders into chunks
    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));
    });

    // Process in parallel
    final totalStopwatch = Stopwatch()..start();
    final results = await Future.wait(
      chunks.map((chunk) => Isolate.run(() => processInIsolate(chunk))),
    );
    totalStopwatch.stop();

    // Aggregate
    int totalProcessed = 0;
    int totalSkipped = 0;
    double totalRevenue = 0;
    final totalCategoryRevenue = <String, double>{};

    print('\nWorker Results:');
    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} processed, '
          '${r.processingTime.inMilliseconds}ms');
    }

    print('\n--- Aggregate Report ---');
    print('Processed:  $totalProcessed orders');
    print('Skipped:    $totalSkipped records');
    print('Revenue:    \$${totalRevenue.toStringAsFixed(2)} USD');
    print('Wall time:  ${totalStopwatch.elapsedMilliseconds}ms');

    print('\nBy Category:');
    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 {
  // Generate sample data
  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);
}
TEXT 📖 Display only
> **Output:** Run in local DartPad or with `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly with different SDK versions.

❓ FAQ

Q: What's the difference between an Isolate and a Thread? A: An Isolate has its own independent heap memory and does not share data; Threads share heap memory. Isolates communicate via message passing, while Threads communicate via shared variables + locks. Isolates have no race conditions.

Q: Is creating an Isolate expensive? A: Creating an Isolate takes about 50-150ms, which is slower than creating a thread. Frequent creation and destruction is inefficient; it's recommended to use an Isolate Pool or Isolate.run (which automatically manages the lifecycle).

Q: What's the difference between Isolate.run and Isolate.spawn? A: Isolate.run is a simplified API that executes a one-shot task and then automatically closes the Isolate. Isolate.spawn creates a persistent Isolate, requiring manual lifecycle and communication management.

Q: Is data copied when transferred between Isolates? A: Yes. All transferred data is deep-copied (except for SendPort). Passing large lists has performance overhead. For very large data, consider transferring in chunks.

Q: How many types of Isolates does Dart have? A: Mainly two: the general-purpose Isolate for parallelism and Flutter's compute for UI isolation. The web platform doesn't support true Isolates and uses Web Workers as a simulation.

Q: Is there an upper limit on the number of Isolates? A: There's no hard limit, but each Isolate occupies about 2MB of memory. In practice, it's recommended not to exceed the number of CPU cores to avoid excessive context switching.

Q: What scenarios are Isolates suitable for? A: CPU-intensive computations (data analysis, image processing, cryptographic calculations). For I/O-intensive tasks, Future/Stream is sufficient and Isolates are not needed.


📖 Summary


📝 Exercises

  1. Basic (⭐): Use Isolate.run to calculate the 42nd Fibonacci number and compare the time with synchronous calculation in the main thread.
  2. Intermediate (⭐⭐): Generate 1,000,000 random numbers, split them into 4 parts, and use 4 Isolates to calculate the sum and average of each part in parallel. Finally, aggregate the results in the main Isolate.
  3. Challenge (⭐⭐⭐): Implement an Isolate Pool using Isolate.spawn: Pre-create N Worker Isolates, have the main Isolate distribute tasks via SendPort, and have Workers return results after processing. Support a task queue and load balancing.

← Previous Lesson | Next Lesson →

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏