YouTip LogoYouTip

Dart Isolates

Concurrency refers to the ability to handle multiple tasks simultaneously. Dart's concurrency model differs from traditional multi-threading; it uses Isolates to implement parallel computing. This chapter introduces Dart's concurrency model, the concept and usage of Isolates, and the message passing mechanism. * * * ## Dart Concurrency Model Most programming languages use a shared-memory multi-threading model, where multiple threads share the same memory space. The drawback of this model is that it easily leads to race conditions and deadlocks. Dart adopts a different strategy: each Isolate has its own independent memory heap, and Isolates do not share memory with each other. They communicate through message passing, which fundamentally avoids data race issues. Dart's concurrency model has three levels: | Level | Mechanism | Applicable Scenarios | | --- | --- | --- | | Event Loop | Single-threaded async (Event Loop) | I/O operations, timers, user interactions | | Isolate | Independent memory + message passing | CPU-intensive computations | | Future/Stream | Async programming syntax | Most daily development scenarios | > Most Dart programs' concurrency needs can be met through async/await and Future/Stream. Isolates are only necessary when you need to perform a large amount of CPU-intensive computation. Introducing Isolates prematurely will make the code more complex with little benefit. * * * ## Isolate Concept and Usage Isolate is Dart's concurrency unit, each Isolate has its own independent memory and event loop. The main program itself runs in an Isolate (the main Isolate). ### Using Isolate.spawn to Create a New Isolate ## Example import'dart:isolate'; // This function will run in the new Isolate // SendPort is used to send messages to the main Isolate void heavyComputation(SendPort sendPort){ print('New Isolate starting computation...'); // Simulate CPU-intensive computation int sum =0; for(int i =1; i <=10000000; i++){ sum += i; } // Send the computation result back to the main Isolate sendPort.send(sum); print('New Isolate computation complete, result sent'); } Future main() async { print('Main Isolate started'); // Create a ReceivePort to receive messages var receivePort = ReceivePort(); // spawn creates a new Isolate await Isolate.spawn(heavyComputation, receivePort.sendPort); print('Main Isolate can do other things while waiting for results...'); // Wait for the computation result from the new Isolate var result = await receivePort.first; print('TUTORIAL computation result: sum of 1 to 10000000 = $result'); receivePort.close(); print('Main Isolate ended'); } Main Isolate startedMain Isolate can do other things while waiting for results...New Isolate starting computation...New Isolate computation complete, result sent TUTORIAL computation result: sum of 1 to 10000000 = 50000005000000Main Isolate ended ### Comparison: Performance Difference With and Without Isolate ## Example // Execute CPU-intensive task in main Isolate (will block) int fibonacci(int n){ if(n <=1)return n; return fibonacci(n -1)+ fibonacci(n -2); } void main(){ // Calculate in main Isolateβ€”β€”will block all other operations var startTime = DateTime.now(); // Note: If n is too large, this computation will be very time-consuming // In actual development, such computations should be placed in a separate Isolate var result = fibonacci(40); var elapsed = DateTime.now().difference(startTime); print('TUTORIAL Fibonacci(40) = $result'); print('Time elapsed: ${elapsed.inMilliseconds}ms'); print('(Note: During computation, main Isolate cannot handle other tasks)'); } > In Flutter applications, if you perform time-consuming synchronous computations in the main Isolate, it will cause UI freezing. The solution is to place the computation task in a separate Isolate, and after computation is complete, pass the result back through messages to update the UI. * * * ## Message Passing Mechanism Isolates communicate with each other through SendPort and ReceivePort. Messages must be serializable (basic types, String, List, Map, etc.), functions or closures cannot be passed. ### Two-way Communication ## Example import'dart:isolate'; // Work function that runs in the new Isolate void workerIsolate(SendPort mainSendPort){ // Create its own ReceivePort to receive messages from main Isolate var workerReceivePort = ReceivePort(); // First send the worker's SendPort to main Isolate to establish a two-way channel mainSendPort.send(workerReceivePort.sendPort); print('Worker Isolate: Waiting for tasks...'); // Listen for tasks sent from main Isolate workerReceivePort.listen((message){ if(message is List){ // Received task: square each number in the list print('Worker Isolate: Received data $message'); var result = message.map((n)=> n * n).toList(); // Send result back through mainSendPort mainSendPort.send(result); }else if(message =='exit'){ print('Worker Isolate: Received exit signal, closing'); workerReceivePort.close(); mainSendPort.send('goodbye'); } }); } Future main() async { print('Main Isolate: Starting'); // Create main receive port var mainReceivePort = ReceivePort(); // Start Worker Isolate await Isolate.spawn(workerIsolate, mainReceivePort.sendPort); // Wait for Worker to send its SendPort (establish two-way communication) SendPort? workerSendPort; await for(var msg in mainReceivePort){ if(msg is SendPort){ workerSendPort = msg; print('Main Isolate: Established two-way communication with Worker'); break; } } // Send tasks through Worker's SendPort workerSendPort!.send([1,2,3,4,5]); workerSendPort.send([10,20,30]); // Receive Worker's computation results int responseCount =0; await for(var msg in mainReceivePort){ if(msg is List){ print('Main Isolate: Received result $msg'); responseCount++; if(responseCount ==2)break; } } // Send exit signal workerSendPort.send('exit'); await for(var msg in mainReceivePort){ if(msg =='goodbye'){ print('Main Isolate: Worker has exited'); break; } } mainReceivePort.close(); print('TUTORIAL Two-way communication demo ended'); } Main Isolate: StartingMain Isolate: Established two-way communication with WorkerWorker Isolate: Waiting tasks...Worker Isolate: Received data [1, 2, 3, 4, 5]Main Isolate: Received result [1, 4, 9, 16, 25]Worker Isolate: Received data [10, 20, 30]Main Isolate: Received result [100, 400, 900]Worker Isolate: Received exit signal, closingMain Isolate: Worker has exited TUTORIAL Two-way communication demo ended ### Using Isolate.run for Simplification (Dart 3.0+) Dart 3.0 introduced Isolate.run(), which greatly simplifies the usage for one-time computation tasks. ## Example import'dart:isolate'; // A time-consuming computation function int complexCalculation(int n){ int result =0; for(int i =0; i < n; i++){ result += i * i; } return result; } Future main() async { print('Starting computation...'); // Isolate.run: One line of code, automatically creates Isolate, executes, returns result var result = await Isolate.run(()=> complexCalculation(10000000)); print('TUTORIAL computation result: $result'); print('Computation complete'); // Comparison: If not using Isolate print('(If computed directly in main Isolate, it would block other operations)'); } Starting computation... TUTORIAL computation result: 333333283333335000000Computation complete(If computed directly in main Isolate, it would block other operations) > Isolate
← Dart Enums And SymbolsDart Async β†’