Một đối tượng std::future có thể được dùng với asych, std::packaged_task và std::promise. Trong bài viết này ta sẽ tập trung vào sử dụng std::future với đối tượng std::promise.
Rất nhiều lần chúng ta phải đối mặt với tình huống muốn thread trả về một kết quả nào đó. Câu hỏi đặt ra là làm cách nào thực hiện được điều này? Giả sử, trong một ứng dụng, chúng ta tạo một thread để nén một thư mục và trả về kết quả thư mục được nén và kích thước của nó.
Bây giờ, ta sẽ thực hiện điều này theo 2 cách,
Cách 1: Chia sẻ data giữa các thread sử dụng pointer
Bằng cách truyền một con trỏ đến thread mới và thread này sẽ set dữ liệu trong đó. Trong khi thread chính chờ bởi một biến điều kiện. Khi thread mới thiết lập dữ liệu và báo hiệu đến biến điều kiện, thì thread chính sẽ thức dậy và lấy dữ liệu từ con trỏ đó.
Để làm một điều đơn giản như trên, chúng ta đã sử dụng một biến điều kiện, một mutex và một con trỏ, tức là 3 thông số để lấy được một giá trị trả về. Bây giờ giả sử chúng ta muốn thread này trả về 3 giá trị khác nhau tại các thời điểm khác nhau thì vấn đề sẽ trở nên phức tạp hơn. Có thể có một giải pháp đơn giản hơn để lấy các giá trị trả về không? Câu trả lời là có bằng cách sử dụng std::future.
Cách 2: Sử dụng std::future và std::promise
std::future là một lớp template và đối tượng của nó lưu trữ giá trị tương lai. Thật vây, một đối tượng std::future lưu trữ bên trong một giá trị sẽ được gán trong tương lai và nó cũng cung cấp một cơ chế để truy cập giá trị đó, tức là sử dụng hàm thành viên get(). Nhưng nếu ai đó cố gắng truy cập giá trị này của tương lai thông qua hàm get() trước khi có sẵn, thì hàm get() sẽ chặn cho đến khi giá trị là khả dụng.
std::promise cũng là một lớp template và đối tượng của nó hứa sẽ set một giá trị trong tương lai. Mỗi đối tượng std::promise có một đối tượng std::future được liên kết tới, đối tương mà sẽ cho ra giá trị khi nó được set bởi std::promise.
Một đối tượng std::promise chia sẻ dữ liệu với đối tượng std::future được liên kết với nó.
Hãy xem xét theo từng bước dưới đây,
Tạo ra một đối tượng std::promise trong thread 1
std::promise<int> promiseObj;
Cho đến bây giờ đối tượng std::promise này không có giá trị liên kết tới . Nhưng nó hứa hẹn rằng ai đó chắc chắn sẽ set giá trị trong đó và khi nó được set thì bạn có thể nhận được giá trị thông qua std::future.
Nhưng bây giờ, giả sử thread 1 đã tạo đối tượng std::promise và chuyển nó đến đối tượng thread 2. Bây giờ làm thế nào để thread 1 có thể biết rằng khi nào threa 2 sẽ set giá trị trong đối tượng std::promise này?
Đó chính là đối tượng std::future, mỗi đối tượng std::promise có một đối tượng std::future, thông qua đó các đối tượng khác có thể lấy giá trị được set bởi std::promise. Vì vậy, thread 1 sẽ tạo đối tượng std::promise và sau đó tìm nạp đối tượng std::future từ nó trước khi chuyển đối tượng std::promise vào cho thread 2.
std::future<int> futureObj = promiseObj.get_future();
Bây giờ thread 1 sẽ chuyển promiseObj cho thread 2. Sau đó, thread 1 sẽ fetch giá trị set bởi thread 2 trong std::promise thông qua hàm get() của std::future.
int val = futureObj.get();
Nhưng nếu giá trị chưa được đặt bởi thread 2, việc gọi này sẽ bị chặn cho đến khi thread 2 set giá trị cho std::promise, tức là
promiseObj.set_value(45);
Hãy xem dòng thực thi hoàn chỉnh trong sơ đồ sau,
Hãy xem ví dụ hoàn chỉnh sử dụng std::future và std::promise,
#include <iostream>
#include <thread>
#include <future>
void initiazer(std::promise<int> * promObj)
{
std::cout<<"Inside Thread"<<std::endl;
promObj->set_value(35);
}
int main()
{
std::promise<int> promiseObj;
std::future<int> futureObj = promiseObj.get_future();
std::thread th(initiazer, &promiseObj);
std::cout<<futureObj.get()<<std::endl;
th.join();
return 0;
}
Nếu đối tượng std::promise bị hủy trước khi set giá trị, hàm get() trên std::future sẽ ném ngoại lệ.
Nếu bạn muốn thread của bạn trả về nhiều giá trị tại các thời điểm khác nhau. Bạn chỉ cần truyền nhiều đối tượng std::promise và fetch nhiều giá trị trả về từ nhiều đối tượng std::future.
Trong các bài viết tới ta sẽ tìm hiểu cách sử dụng std::future với std::asych và std::packaged_task.
Để lại một bình luận