Để xây dựng ứng dụng Flutter có hiệu năng tốt, bạn phải nắm vững nghệ thuật giữ cho main UI thread luôn thông thoáng. Điều này tất yếu dẫn đến hai khái niệm: concurrency* và *parallelism. Dù nhiều developer dùng hai thuật ngữ này thay thế cho nhau, trong thế giới Dart và Flutter, chúng có ý nghĩa hoàn toàn khác nhau và được xử lý bằng các cơ chế hoàn toàn riêng biệt.
Hãy cùng phân tích sự khác biệt và xem cách offload công việc đúng cách trong Flutter.
Mục Lục

---
Khái Niệm Cốt Lõi: Concurrency vs. Parallelism
Dart, theo mặc định, là ngôn ngữ single-threaded. Nó hỗ trợ concurrency sẵn có, nhưng bạn phải chủ động yêu cầu nó thực hiện parallelism.
---
Concurrency trong Dart: Event Loop và Futures
Khi một Flutter app khởi động, nó tạo ra một luồng thực thi duy nhất gọi là Main Isolate. Main isolate này chứa một Event Loop. Nhiệm vụ của Event Loop rất đơn giản: liên tục kiểm tra một hàng đợi các event (như tap của người dùng, repaint UI, hay response từ network) và xử lý chúng từng cái một.
Khi bạn dùng async, await, và Future, bạn đang tận dụng concurrency. Bạn đang nói với Event Loop: "Tôi sẽ bắt đầu task này, nhưng nó cần một lúc mới xong (như chờ network response). Cứ đi làm việc khác đi, tôi sẽ báo khi xong."
// The Event Loop is free to update the UI while waiting for the network
Future<void> fetchChatMessages() async {
final response = await apiServer.get('/messages');
// ... process response
}
---
Bẫy: Khi Futures Làm Block UI
Nguy hiểm xảy ra khi developer nhầm tưởng async nghĩa là "chạy trên background thread." Không phải vậy.
Nếu bạn await một tác vụ nặng, tốn nhiều CPU (CPU-bound task) trong một Future, Event Loop buộc phải thực thi phép tính nặng đó trên main thread ngay khi Future resolve.
Hãy tưởng tượng bạn đang xây dựng một ứng dụng chat phức tạp hay một app nhật ký kỹ thuật số phong phú. Nếu app tải về một payload JSON khổng lồ, lồng nhau nhiều tầng chứa lịch sử người dùng, bản thân network request đã rất nhanh (I/O bound). Tuy nhiên, việc parse chuỗi JSON đó thành các Dart object đòi hỏi CPU thực sự. Nếu parse mất 200 millisecond, Event Loop bị block trong 200 millisecond đó. Vì Flutter hướng đến render một frame mỗi 16ms (để đạt 60fps), app của bạn vừa bỏ lỡ hàng chục frame.
Đây là lúc bạn cần parallelism thực sự.
---
Parallelism trong Dart: Isolates Ra Đời
Để đạt được parallelism — để thuê thêm đầu bếp thứ hai — Dart sử dụng Isolates.
Khác với thread trong các ngôn ngữ như Java hay C++, Dart Isolates không chia sẻ bộ nhớ. Mỗi Isolate có vùng bộ nhớ riêng biệt và Event Loop riêng của nó. Vì không chia sẻ bộ nhớ, bạn tránh được những ác mộng multi-threading kinh điển như deadlock hay race condition. Thay vào đó, các Isolate giao tiếp bằng cách truyền message qua lại.
---
Nền Tảng: SendPort và ReceivePort
Trước khi có các wrapper cấp cao tiện lợi, quản lý isolate có nghĩa là phải tự tay làm việc với các communication channel nền tảng: Ports. Vì isolate không chia sẻ bộ nhớ, chúng giao tiếp như hai server riêng biệt nói chuyện qua kết nối mạng.
Để làm điều này, bạn dùng ReceivePort (để lắng nghe message đến) và SendPort (địa chỉ chính xác bạn đưa cho người khác để họ gửi message về ReceivePort của bạn).
import 'dart:isolate';
// 1. The top-level entry point for the new isolate.
// It MUST take a SendPort as an argument to know where to send data back.
void parseHeavyJson(SendPort mainSendPort) {
// Simulate heavy lifting...
final parsedData = ["Message 1", "Message 2", "Message 3"];
// Send the result back to the main thread
mainSendPort.send(parsedData);
}
Future<void> loadData() async {
// 2. Create a port on the main thread to listen for the result
final receivePort = ReceivePort();
// 3. Spawn the isolate, handing it the SendPort so it can reply to us
await Isolate.spawn(parseHeavyJson, receivePort.sendPort);
// 4. Wait for the first message to come through our ReceivePort
final result = await receivePort.first;
print(result); // Outputs: [Message 1, Message 2, Message 3]
// 5. Clean up the port when we are done
receivePort.close();
}
---
Cách Flutter Cổ Điển: compute()
Trong nhiều năm, cách tiêu chuẩn để đạt parallelism nhanh trong Flutter là hàm compute() do thư viện foundation của Flutter cung cấp. Nó ẩn đi toàn bộ boilerplate của việc tự spawn isolate và thiết lập two-way communication port.
import 'package:flutter/foundation.dart';
import 'dart:convert';
// 1. A top-level function that runs in the background
List<Message> parseMassiveJson(String jsonString) {
final List<dynamic> parsed = jsonDecode(jsonString);
return parsed.map((e) => Message.fromJson(e)).toList();
}
// 2. The function called by your UI
Future<void> loadChatHistory(String jsonPayload) async {
// compute() spins up an isolate, runs the function, returns the result, and kills the isolate
final messages = await compute(parseMassiveJson, jsonPayload);
updateUI(messages);
}
---
Cách Dart Hiện Đại: Isolate.run()
Trong Dart 2.19, ngôn ngữ giới thiệu Isolate.run(). Về mặt chức năng, nó rất tương tự compute(), nhưng thuộc về pure Dart (dart:isolate) thay vì bị ràng buộc với Flutter framework.
import 'dart:isolate';
Future<void> loadChatHistory(String jsonPayload) async {
// Pass the heavy function and the data to a new Isolate directly
final messages = await Isolate.run(() => parseMassiveJson(jsonPayload));
updateUI(messages);
}
---
Nâng Cấp: Quản Lý Isolate Pool với squadron
Dù Isolate.run() và compute() cực kỳ tiện lợi, chúng ẩn một chi phí thầm lặng: thời gian khởi động Isolate. Spin up một isolate mới tốn bộ nhớ và vài millisecond. Nếu app của bạn liên tục trigger các background task nặng, điều này có thể trở thành bottleneck.
Đây là lúc package squadron* tỏa sáng. Nó xử lý *worker pool, cho phép bạn khởi tạo các worker isolate chạy thường trực ở background.
// A simplified conceptual example of Squadron in action
final pool = MyWorkerPool(concurrencySettings: ConcurrencySettings(maxWorkers: 4));
// Start the pool (spin up the isolates once)
await pool.start();
// Send tasks to the pool - these are distributed among the active workers
final result1 = await pool.processImage(image1);
final result2 = await pool.processImage(image2);
---
Nguyên Tắc Vàng
async / await / Future).
Nếu bạn đang làm việc nặng* (tính toán, parsing, xử lý ảnh): Dùng Parallelism.
* Với tác vụ một lần, dùng Isolate.run() hoặc compute().
* Với tác vụ liên tục hoặc tần suất cao, dùng worker pool package như squadron.Nắm vững sự phân biệt này là ranh giới giữa một Flutter app cảm giác "tạm ổn" và một app thực sự mượt mà như native, responsive, và chuyên nghiệp. Bảo vệ main thread, offload các tác vụ nặng, và để UI của bạn tỏa sáng!
Loading…