1. Tổng quan.

            Dart hỗ trợ chương trình chạy đồng thời với async-await, isolates and các class như Future và Stream.

            Với một ứng dựng, tất cả code của Dart chạy trong main isolate. Mỗi isolate có một thread và sẽ không chia sẻ các đối tương có thể biến đổi với các isolates khác. Các isolate truyền đạt thông tin với nhau thông qua message.

            Nhiều Dart app chỉ sử dụng main isolate, nhưng chúng ta có thể tạo thêm các isolate cho phép thực thi đồng thời nhiều event trên nhiều lõi bộ xử lý.

2. Các loại và cú pháp của bất đồng bộ.

2.1. Future and Stream types

            Ngôn ngữ và các thư viện Dart sử dụng các đối tượng Future và Stream để các giá trị tương ứng sẽ được cung cấp trong tương lai. Ví dụ, tương ứng với giá trị kiểu int là Future<int> và Stream<int>.

            Tại sao quan trọng phương thức là đồng bộ hay bất đồng bộ? Điều này quan trọng bởi vì hầu hết các app sẽ cần thực hiện nhiều công việc trong cùng một thời gian. Ví dụ: một ứng dụng có thể bắt đầu một yêu cầu HTTP, nhưng cần cập nhật màn hình của ứng dụng đó hoặc phản hồi thông tin đầu vào của người dùng trước khi yêu cầu HTTP hoàn tất. Phương thức bất đông bộ sẽ giúp ứng dụng luôn phản hồi.

2.2. Async-await

            Async-await cung cấp một các khai báo để xác định các hàm không đồng bộ và sử dụng kết quả của chúng.

Trong ví dụ trên, hàm main () sử dụng từ khóa await phía trước _readFileAsync() để cho phép mã Dart khác (chẳng hạn như trình xử lý sự kiện) sử dụng CPU trong khi native code (file I/O ) thực thi. Việc sử dụng await cũng có tác dụng chuyển đổi Future < String > được trả về bởi _readFileAsync() thành String. Kết quả là, biến nội dung có kiểu ngầm định là String.

3. Cách các isolate hoat động.

Bằng cách sử dụng các isolate, code Dart có thể thực hiện nhiều tác vụ độc lập cùng một lúc, sử dụng các lõi xử lý bổ sung nếu chúng có sẵn. Các isolate giống như các luồng hoặc quy trình, nhưng mỗi isolate có bộ nhớ riêng, một luồng duy nhất chạy một vòng lặp.

3.1. Main isolate

Một ứng dụng Dart điển hình thực thi tất cả mã của nó trong main isolate của ứng dụng, như thể hiện trong hình sau:

Ngay cả các chương trình single-isolate cũng có thể thực thi trơn tru bằng cách sử dụng async-await để đợi các hoạt động không đồng bộ hoàn tất trước khi tiếp tục sang công việc tiếp theo. Một ứng dụng hoạt động tốt sẽ bắt đầu nhanh chóng, truy cập vào vòng lặp sự kiện càng sớm càng tốt. Sau đó, ứng dụng sẽ phản hồi nhanh chóng với từng sự kiện được xếp hàng đợi, sử dụng các hoạt động không đồng bộ nếu cần.

3.2. Vòng đời của isolate

Như hình trên cho thấy, mọi isolate bắt đầu bằng cách chạy một số code Dart, chẳng hạn như hàm main(). Code Dart này có thể đăng ký một số trình xử lý sự kiện – để phản hồi thông tin đầu vào của người dùng hoặc file I/O như hình. Khi function khởi tạo của isolate quay trả lại, isolate vẫn tồn tại nếu nó cần xử lý các sự kiện tiếp theo. Sau khi xử lý các sự kiện, isolate sẽ thoát ra.

3.3. Xử lý sự kiện.

Trong ứng dụng, hàng đợi sự kiện của main isolate có thể chứa các yêu cầu repaint và các thông báo của tap và các sự kiện giao diện người dùng khác. Ví dụ: hình sau cho thấy một sự kiện repaint, tiếp theo là tap event, tiếp theo là hai sự kiện repaint. Vòng lặp sự kiện nhận các sự kiện từ hàng đợi theo thứ tự xuất trước, nhập trước.

Nếu một hoạt động đồng bộ mất quá nhiều thời gian xử lý, ứng dụng có thể không phản hồi. Trong hình dưới, sự kiện tap mất quá nhiều thời gian, vì vậy các sự kiện tiếp theo được xử lý quá muộn. Ứng dụng có vẻ như bị đóng băng và bất kỳ hoạt ảnh nào mà nó thực hiện có thể bị giật (jerky). Tệ hơn, giao diện người dùng có thể trở nên hoàn toàn không phản hồi.

3.4. Background workers

            Nếu giao diện người dùng của ứng dụng của bạn không phản hồi do tính toán tốn nhiều thời gian như: parsing a large JSON file, hãy xem xét tách ra sử dụng worker isolate, thường được gọi là background worker. Một trường hợp phổ biến, được hiển thị trong hình sau, là sinh ra worker isolate đơn thực hiện tính toán và sau đó thoát ra. Worker isolate trả về kết quả của nó trong một thông báo khi isolate thoát ra.

Mỗi isolate message có thể gửi một đối tượng, bao gồm bất kỳ thứ gì có thể truy cập chuyển tiếp từ đối tượng đó. Không phải tất cả các loại đối tượng đều có thể gửi được và việc gửi không thành công nếu bất kỳ đối tượng có thể truy cập chuyển tiếp nào không thể gửi được. Ví dụ: bạn chỉ có thể gửi một đối tượng kiểu List<Object> nếu không có đối tượng nào trong danh sách là không thể điều chỉnh được. Nếu một trong các đối tượng là Socket, thì quá trình gửi không thành công vì các socket không thể gửi được.

Note: Trong Flutter chúng ta có thể sử dụng function compute() để lấy giá trị trong tương lai tương ứng, tuy nhiên cần lưu ý là  đối số gọi lại phải là một hàm cấp cao nhất, không phải là một closure hoặc một instance hoặc phương thức tĩnh của một lớp.

4. Ví dụ:

Hàm _parseInBackground() chứa code sinh ra (tạo và bắt đầu) vùng worker isolate, sau đó trả về kết quả:

  1. Trước khi tạo ra worker isolate, khởi tạo ra một cổng nhận (Receiver), cho phép worker isolate gửi tin nhắn đến main isolate.
  2. Tiếp theo là lệnh gọi Isolate.spawn(), tạo và bắt đầu isolate cho background worker. Đối số đầu tiên của Isolate.spawn() là hàm mà worker isolate thực thi: _readAndParseJson. Đối số thứ hai là SendPort mà worker isolate có thể sử dụng để gửi tin nhắn đến main isolate. Không khởi tạo SendPort; nó sử dụng thuộc tính sendPort của ReceivePort.
  3. Sau khi isolate được sinh sản, main isolate sẽ đợi kết quả. Bởi vì lớp GetPort triển khai Stream, thuộc tính đầu tiên là con đường dễ dàng để lấy một giá từ worker isolate.

Code:

Mô hình làm việc của ví dụ trên:

5. Tổng kết.

Trên đây là một số chia sẻ về việc sử dụng Concurrency trong Dart nhằm tăng hiệu suất cho ứng dựng. Mong rằng qua bài viết sẽ giúp ích cho các bạn phần nào đó.

Cảm ơn các bạn đã đọc bài viết của mình.

Author: DuongVT19

Leave a Comment

* By using this form you agree with the storage and handling of your data by this website.