Cả std::async
và std::thread
trong C++ đều được sử dụng để thực thi tác vụ bất đồng bộ (concurrent execution), nhưng chúng có sự khác biệt quan trọng trong cách quản lý luồng và xử lý kết quả.
1. std::thread
– Luồng thấp cấp (Low-Level Thread)
std::thread
là một API cấp thấp để tạo và quản lý luồng trong C++.- Khi sử dụng
std::thread
, lập trình viên phải tự quản lý vòng đời của luồng, chẳng hạn như chờ (join()
) hoặc tách (detach()
) luồng để tránh lỗi truy cập bộ nhớ. - Không có cơ chế tích hợp để lấy kết quả từ hàm chạy trên luồng.
Ví dụ:
#include <iostream>
#include <thread>
void printMessage() {
std::cout << "Hello from thread!" << std::endl;
}
int main() {
std::thread t(printMessage);
t.join(); // Đợi luồng hoàn thành
return 0;
}
✅ Ưu điểm:
- Kiểm soát tốt hơn đối với việc tạo và quản lý luồng.
- Thích hợp cho các ứng dụng cần kiểm soát chi tiết về scheduling và synchronization.
❌ Nhược điểm:
- Cần xử lý kết quả thủ công (nếu có).
- Có thể gây ra lỗi nghiêm trọng nếu không gọi
join()
hoặcdetach()
. - Không có tích hợp với các cơ chế
future
hoặcpromise
.
2. std::async
– Tương lai & Quản lý luồng tự động
std::async
cung cấp một cách tiếp cận mức cao hơn để thực thi tác vụ bất đồng bộ bằng cách sử dụngstd::future
.- Nó tự động quản lý vòng đời của luồng, giúp tránh lỗi quản lý luồng như quên
join()
. - Cho phép lấy kết quả từ luồng bằng
std::future
.
Ví dụ:
#include <iostream>
#include <future>
int compute() {
return 42;
}
int main() {
std::future<int> result = std::async(std::launch::async, compute);
std::cout << "Result: " << result.get() << std::endl; // Lấy kết quả từ async
return 0;
}
✅ Ưu điểm:
- Dễ sử dụng hơn do tích hợp với
std::future
, giúp lấy kết quả dễ dàng. - Quản lý vòng đời của luồng tự động, không cần
join()
. - Hỗ trợ
std::launch::deferred
, cho phép trì hoãn việc chạy hàm.
❌ Nhược điểm:
- Không kiểm soát được chi tiết về luồng như
std::thread
. - Hiệu suất có thể không cao bằng
std::thread
trong một số trường hợp.
3. So sánh chính
Tiêu chí | std::thread |
std::async |
---|---|---|
Cấp độ | Thấp (Low-Level) | Cao (High-Level) |
Quản lý luồng | Phải tự gọi join() hoặc detach() |
Tự động quản lý luồng |
Trả về giá trị | Không có sẵn, cần dùng std::promise/std::future |
Dùng std::future để nhận kết quả |
Kiểm soát thực thi | Linh hoạt hơn | Dễ sử dụng hơn |
Khả năng trì hoãn | Không có | Có thể sử dụng std::launch::deferred |
4. Khi nào nên dùng gì?
- Dùng
std::thread
khi:- Cần kiểm soát chi tiết về luồng.
- Không cần lấy kết quả trả về từ hàm.
- Cần hiệu suất cao với quản lý luồng thủ công.
- Dùng
std::async
khi:- Cần thực thi bất đồng bộ nhưng muốn quản lý luồng tự động.
- Cần lấy kết quả từ hàm một cách dễ dàng.
- Muốn tận dụng
std::launch::deferred
để trì hoãn thực thi.
5. Lưu ý về std::launch::deferred
Khi sử dụng std::async
, nếu không chỉ định std::launch::async
, mặc định nó có thể chạy ở chế độ:
std::launch::async
: Chạy ngay lập tức trên một luồng riêng.std::launch::deferred
: Chỉ chạy khi gọifuture.get()
, tức là thực thi trên luồng chính.
Ví dụ:
std::future<int> result = std::async(std::launch::deferred, compute);
Trong trường hợp này, compute()
sẽ không chạy ngay lập tức mà chỉ chạy khi result.get()
được gọi.
6. Tóm tắt
std::thread
→ Dành cho lập trình viên muốn kiểm soát luồng chi tiết.std::async
→ Dành cho lập trình viên muốn đơn giản hóa việc thực thi bất đồng bộ, đặc biệt khi cần lấy kết quả.