Mộc Viên's Blog Mộc Viên's Blog
Giải thích sự khác biệt giữa std::async và std::thread.

Giải thích sự khác biệt giữa std::async và std::thread.

Ngày đăng:

Giải thích sự khác biệt giữa std::async và std::thread.

Cả std::asyncstd::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ặc detach().
  • Không có tích hợp với các cơ chế future hoặc promise.

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ụng std::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ọi future.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ả.

Gần đây