C++ Multithreading & Process
1. Cơ bản về Thread
std::thread dùng để tạo và chạy luồng.
void task() { std::cout << "Hello from thread!\n"; }
std::thread t(task);
t.join(); // hoặc t.detach();
🔸 Nên gọi
join() hoặc detach() để tránh terminate chương trình.
2. Đồng bộ hóa: Mutex và Condition Variable
std::mutex m;
int shared = 0;
void inc() {
std::lock_guard lock(m);
++shared;
}
std::condition_variable cv;
bool ready = false;
void worker() {
std::unique_lock lock(m);
cv.wait(lock, [] { return ready; });
// do work
}
3. Future, Promise, Async
std::promise gửi dữ liệu từ thread A sang B, dùng kèm std::future.
std::promise p;
std::future f = p.get_future();
std::thread([&p]{ p.set_value(42); }).detach();
int result = f.get();
std::async tạo thread và tự động quản lý future.
std::future f = std::async([]{ return 5 + 3; });
int result = f.get();
4. Khi nào nên dùng thread?
- Thực thi tác vụ tốn thời gian (I/O, xử lý ảnh, tính toán lớn).
- Song song hoá các phần độc lập trong ứng dụng.
- Giữ UI responsive (trong GUI hoặc game).
5. Lưu ý về hiệu năng và tài nguyên
- Không tạo quá nhiều thread: giới hạn bởi số core và tài nguyên OS.
- Dùng thread pool (ví dụ
std::asynchoặc lib thread pool). - Tránh deadlock bằng cách luôn lock theo thứ tự xác định.
6. Những vấn đề thường gặp với thread
- Không gọi join/detach: thread bị "std::terminate" khi hủy.
- Data race: hai thread truy cập chung 1 biến không được bảo vệ mutex.
- Deadlock: hai thread lock chéo lẫn nhau và không thoát được.
- Lỗi truy cập vùng nhớ sau khi thread kết thúc (dangling reference).
- Thread leak: tạo thread liên tục mà không quản lý.
- Không xử lý exception trong thread: thread chết âm thầm.
📌 Cách xử lý và phòng tránh:
- Dùng
std::lock_guardhoặcstd::unique_lockđể đảm bảo mutex luôn được unlock an toàn. - Luôn gọi
join()hoặcdetach()với mỗi thread được tạo. - Sử dụng
try-catchtrong thread để bắt exception và xử lý. - Dùng
std::atomicnếu chỉ cần thay đổi đơn giản mà không cần mutex. - Tránh lock nhiều mutex cùng lúc, hoặc lock theo thứ tự cố định.
7. Làm việc với Process
- Process là một chương trình đang được thực thi, có bộ nhớ, PID, và tài nguyên riêng biệt.
- Giao tiếp giữa các process (IPC) thường dùng:
pipe,shared memory,message queue,socket. - Dùng
fork()để tạo process con trên Linux. - Dùng
exec()để thay thế image của process hiện tại bằng chương trình khác.
pid_t pid = fork();
if (pid == 0) {
// process con
execlp("ls", "ls", "-l", nullptr);
} else {
wait(NULL);
}
🔹 Lưu ý: Mỗi process có vùng nhớ tách biệt, cần IPC để trao đổi dữ liệu. Dùng signal hoặc socket cho thông báo bất đồng bộ.
8. So sánh Thread và Process
| Tiêu chí | Thread | Process |
|---|---|---|
| Định nghĩa | Đơn vị nhỏ nhất trong thực thi | Chương trình đang chạy |
| Không gian nhớ | Chia sẻ bộ nhớ với thread khác | Không gian nhớ tách biệt |
| Tài nguyên | Chia sẻ file, heap | Tài nguyên riêng biệt |
| Tốc độ tạo | Nhanh | Chậm hơn |
| Giao tiếp | Dễ (qua biến dùng chung) | Cần IPC (pipe, shm, socket...) |
| Tác động lỗi | Có thể ảnh hưởng toàn bộ process | Chỉ ảnh hưởng process đó |
| Ví dụ dùng | Song song hoá xử lý UI, logic | Chạy chương trình độc lập |
✅ Khi nào dùng thread?
- Khi muốn song song hóa nội bộ ứng dụng.
- Khi cần chia sẻ bộ nhớ, xử lý nhanh và nhẹ.
- Khi cần cách ly độc lập giữa các phần.
- Khi xử lý các tác vụ lớn, cần độ ổn định cao hoặc crash-safe.