C++11 giới thiệu thư viện <thread> cùng các cơ chế đồng bộ hóa mạnh mẽ như std::mutex, std::promise, và std::future, giúp phát triển ứng dụng đa luồng an toàn và hiệu quả. Dưới đây là tổng quan thực tiễn về cách khai báo, khởi chạy, đồng bộ và thu thập kết quả từ nhiều luồng.
Khởi tạo Luồng với std::thread
Đối tượng std::thread được khởi tạo bằng hàm mục tiêu (có thể là hàm toàn cục, hàm tĩnh, hoặc hàm thành viên) cùng danh sách tham số tương ứng. Lưu ý rằng luồng phải được quản lý rõ ràng — hoặc join() để chờ kết thúc, hoặc detach() để tách độc lập.
#include <thread>
#include <iostream>
void compute_task(int id) {
std::cout << "Task " << id << " running on thread "
<< std::this_thread::get_id() << "\n";
}
int main() {
std::thread t1(compute_task, 1);
std::thread t2(compute_task, 2);
t1.join(); // Chờ t1 hoàn tất
t2.join(); // Chờ t2 hoàn tất
}
Gọi Hàm Thành Viên Từ Luồng
Khi gọi hàm thành viên lớp, cần truyền rõ ràng con trỏ this và sử dụng std::mem_fn hoặc cú pháp trực tiếp với con trỏ hàm thành viên:
class Calculator {
public:
void add(int a, int b) {
result = a + b;
std::cout << "Sum: " << result << "\n";
}
private:
int result;
};
int main() {
Calculator calc;
std::thread t(&Calculator::add, &calc, 5, 7);
t.join();
}
Quản Lý Nhiều Luồng Qua std::vector
Vì std::thread không hỗ trợ sao chép (non-copyable), việc lưu trữ vào std::vector yêu cầu chuyển quyền sở hữu bằng std::move:
#include <vector>
#include <thread>
void worker(int id) { /* ... */ }
int main() {
std::vector<std::thread> pool;
for (int i = 0; i < 4; ++i) {
pool.emplace_back(worker, i); // Emplace trực tiếp — không cần move
}
for (auto& t : pool) {
if (t.joinable()) t.join();
}
}
Truyền Dữ Liệu Trở Về Từ Luồng
Thay vì dùng con trỏ chung dễ gây race condition, nên ưu tiên cơ chế std::promise/std::future:
#include <future>
#include <thread>
int compute_sum(int a, int b) {
return a + b;
}
int main() {
std::promise<int> p1, p2;
std::future<int> f1 = p1.get_future();
std::future<int> f2 = p2.get_future();
std::thread t1([&](int x, int y) { p1.set_value(x + y); }, 10, 20);
std::thread t2([&](int x, int y) { p2.set_value(x * y); }, 4, 5);
t1.join();
t2.join();
std::cout << "Sum: " << f1.get() << ", Product: " << f2.get() << "\n";
}
Chọn Giữa join() và detach()
join(): Luồng hiện tại bị chặn cho đến khi luồng con kết thúc — phù hợp khi cần đảm bảo thứ tự thực thi hoặc thu thập kết quả.detach(): Luồng con chạy độc lập; hệ điều hành giải phóng tài nguyên khi nó kết thúc — chỉ dùng khi luồng không phụ thuộc vào ngữ cảnh hiện tại (ví dụ: ghi log nền).
Lưu ý: Gọi join() hoặc detach() đúng một lần trên mỗi đối tượng std::thread; vi phạm sẽ gây lỗi runtime (std::terminate).