Đa luồng trong Flutter 

Trước khi vào bài viết thì mình xin giới thiệu 1 chút về Flutter:

Flutter là Mobile UI Framework được Google phát hành vào năm 2017, được sử dụng để phát triền giao diện các ứng dụng di động trên các nền tảng Android/ iOS/ MacOs/……

Bất kỳ ngôn ngữ lập trình nào thì cũng phải tìm cách xử lý khi chạy những task nặng, tốn thời gian…Thì khi đó việc lập trình bất đồng bộ hoặc xử lý chúng trong background được tạo ra, thay vì xử lý trên Main thread thì việc gây giật UI hoặc ứng dụng chạy chậm, gây nóng máy làm khó chịu cho người sử dụng.

Và cũng là một UI Framework, Flutter đã cung cấp Stream và Future để hỗ trợ chúng ta trong việc giải quyết các vấn đề trong lập trình bất đồng bộ

  1. Stream: 
  1. Thực chất Stream là một luồng data bất đồng bộ, nó giống như 1 cái ống gồm 1 đầu nhận dữ liệu và đầu kia sẽ là dữ liệu khi nó xử lý xong
  2. Cấu trúc của 1 Stream
  • Để handle một Stream thì ta có StreamController.
  • Để đẩy dữ liệu vào Stream thì thông qua thuộc tính sink(có thể đẩy bất kỳ dữ liệu nào vào Stream: value, object, collection….)
  • Để publish dữ liệu ra ngoài thì chúng ta dùng thuộc tính stream,  và dùng hàm listen để lắng nghe dữ liệu trả về

Như ví dụ dưới đây, chúng ta sẽ tạo 1 danh sách có 10 phần tử, sau mỗi lần chạy thì sẽ add dữ liệu đó vào Stream.

class _StreamWidgetExampleState extends State<StreamWidgetExample> {
 final StreamController _counterController = StreamController<int>();
 Stream get _counterStream => _counterController.stream;
 late StreamSubscription _subscription;

  _increment() {
    List.generate(10, (data) {
      _counterController.sink.add(data);
    });
  }

  @override
  void initState() {
    super.initState();
    _increment();
    _subscription = _counterStream.listen((data) {
      print("Receive data $data");
    });
  }

  @override
  void dispose() {
    _subscription.cancel();
    _counterController.close();
    super.dispose();
  }
}

và đây là kết quả khi chạy function trên

2. Future

  • Future là một hàm thực thi cho các hoạt động bất đồng bộ, và có 2 trạng thái đó là Completed UnCompleted
  • Uncompleted: khi bạn gọi một hàm không đồng bộ, nó trả về một uncompleted future. Future này đang chờ cho các hoạt động không đồng bộ của hàm kết thúc hoặc trả về một error.
  • Completed: nếu một hành động không đồng bộ thành công, future sẽ hoàn thành với một giá trị. Nếu không nó hoàn thành với một error.
    • Hoàn thành với một giá trị: một future của kiểu Future<T> hoàn thành với một giá trị thuộc kiểu T. Ví dụ, một future với kiểu Future<String> thì nó sẽ trả về một giá trị string. Nếu một future không cung cấp một kiểu giá trị nào thì kiểu của future là Future<void>.
    • Hoàn thành với một error: nếu một hoạt động bất đồng bộ được thực hiện bởi một hàm thất bại vì bất kỳ lý do gì thì future hoàn thành với một error.
  • Khi sử dụng hàm Future thì bắt buộc phải có từ khoá asyncawait:

Khi tạo 1 hàm bất đồng bộ thì ta phải thêm từ khoá async trước thân hàm:

  • async: Bạn có thể sử dụng từ khóa async trước thân hàm bất đồng bộ.
  • async function: là một function được đánh dấu bởi từ khóa async.
  • await: bạn có thể sử dụng từ khóa await để lấy kết quả từ một việc bất đồng bộ. Từ khóa await chỉ được sử dụng với hàm async.
 futureExampleFunc() async {
    //do something
 }

Đây là hàm không có kiểu dữ liệu trả về

  Future<Auth> futureFunc() async {
    //do something
    return Auth();
  }

Còn đây là hàm có kiểu dữ liệu trả về


  initData() async {
    var auth = await futureFunc();
    print(auth);
  }

Bây giờ chỉ cần thêm await khi chờ hàm này thực thi xong thì sẽ gán dữ liệu cho auth

Đây là sơ đồ hoạt động của 1 Async Function

Mặc dù Stream và Future đã hỗ trợ rất tốt trong việc lập trình bất đồng bộ, nhưng việc này vẫn chưa giải quyết được triệt để vấn đề vì chúng đều thực hiện trên luồng chính. Vì vậy Isolate được đưa ra như là một giải pháp cho việc này, bằng việc thực hiện các việc trên dưới background.

3. Isolate

Sơ đồ hoạt động của Isolate

Isolate là một tham chiếu đến một thread, thường khác với main thread hiện tại. Hay có thể nói dễ hiểu hơn thì Isolate là một phiên bản tương ứng của Thread trên ngôn ngữ lập trình Dart. Nó tương tự với Vòng lặp sự kiện (Event Loop) nhưng có một số điểm khác biệt như sau:

  • Nó là 1 thread với bộ nhớ riêng, biệt lập.
  • Nó không thể chia sẻ trực tiếp dữ liệu với các thread khác.
  • Bất kể dữ liệu nào được truyền giữa các thread đều bị trùng lặp.

Mỗi Isolate có một vòng lặp sự kiện (Event Loop) của riêng mình nhờ đó chúng sẽ hoạt động song song và độc lập với nhau.

Hãy xem ví dụ dưới đây để có thể hiểu rõ hơn cách hoạt động của 1 Isolate. Chúng ta tạo ra 1 Isolate để xử lý cho việc chạy 1 vòng lặp từ 1 -> 1000000, sau khi chạy xong vòng lặp sẽ kết thúc và trả dữ liệu về cho hàm gọi nó.

void myIsolate(SendPort sendPort) {
  int data = 0;
  for (int i = 0; i <= 1000000; i++) {
    data += i;
  // sau mỗi vòng lặp thì data sẽ cộng thêm giá trị của biến i
  }
  //sau khi kết thúc vòng lặp sẽ gọi hàm exit để kết thúc isolate này và trả data về
  Isolate.exit(sendPort, data);
}
  void _runMyIsolate() async {
    var receivePort = ReceivePort();
  // đăng ký một isolate thông qua hàm spawn, ở đây ta sẽ truyền hàm xử lý dữ liệu và 1 port
    await Isolate.spawn(myIsolate, receivePort.sendPort);
    print(await receivePort.first);
  }

Chúng ta có thể thấy khi thực hiện việc tạo 1 thread isolate là thông qua method Isolate.spawn thì lúc này ứng dụng sẽ tạo ra 1 thread với tên là myIsolate chạy song song với luồng main, và luồng này sẽ bị đóng lại khi sử dụng method Isolate.exit.

4. Kết luận

Flutter ( Dart ) là Single-Thread , do đó để làm hài lòng người dùng, các nhà phát triển phải đảm bảo rằng ứng dụng sẽ chạy trơn tru nhất có thể. Stream, FuturesIsolates là những công cụ rất mạnh có thể giúp bạn đạt được mục tiêu này.

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like