Xử lý bất đồng bộ (Asynchronous) với std::future và std::async từ C++11

Trong C++ cũng như bất kỳ ngôn ngữ nào khác, khi chúng ta cần xử lý tính toán tốn thời gian thì tốt nhất là nên tạo một thread dành riêng cho xử lý tính toán đó (thường gọi là worker thread – phân biệt với GUI thread) để tránh gây ảnh hưởng đến GUI thread làm cho trải nghiệm người dùng kém đi. Với C++ thì từ C++11 trở đi chúng ta đã có thể tạo thread cực kỳ easy sử dụng std::thread như sau →

trong đó perform_longtime_computation() là hàm xử lý tính toán tốn thời gian. Chúng ta có một worker thread riêng để chạy hàm perform_longtime_computation() và trả kết quả về biến result.

Ở đây chúng ta đang muốn xử lý song song (multithreading) nên trên main thread chúng ta cần phải làm việc khác trong lúc worker thread xử lý tính toán chứ không thể đứng đó đợi được. Vấn đề là trên main thread làm sao để chúng ta biết khi nào hàm perform_longtime_computation() được thực thi xong trên worker thread để lấy kết quả về ? Hãy cùng xem giải pháp.


Phương pháp truyền thống là sử dụng một biến global để lưu kết quả tính toán bởi worker thread

Kết quả tính toán sẽ được lưu vào biến global gResult và main thread sẽ đọc giá trị của biến này ra. Để đồng bộ giữa các thread chúng ta sẽ cần sử dụng thêm cơ chế đồng bộ như: mutex, semaphore hoặc conditional variable, etc.


Một phương pháp implement dễ dàng hơn mà mình muốn giới thiệu trong bài này là sử dụng std::future<T> kết hợp với std::async được hỗ trợ từ C++11 →

Với phương pháp này chúng ta không cần dùng đến biến global, cũng không cần sử dụng trực tiếp mutex, semaphore, etc. Biến furtureResult là biến local, nó không chứa giá trị tính toán trực tiếp của worker thread (tạo ra bởi std::async) mà đằng sau nó là một cơ chế đồng bộ, nó đảm bảo rằng hàm get() sẽ  chỉ trả về kết quả nếu worker thread đã tính toán xong rồi. Nếu worker thread chưa tính toán xong thì hàm get() sẽ block cho đến khi worker thread thực hiện xong công việc tính toán của nó.

— Phạm Minh Tuấn (Shun) —