Giao diện
🧵 std::thread — Spawning & Joining Threads
std::thread là building block cơ bản để tạo concurrent execution trong C++. Tạo Thread đầu tiên
Hello World — Multithreaded Edition
cpp
#include <iostream>
#include <thread>
void sayHello() {
std::cout << "Hello from thread!\n";
}
int main() {
// Tạo thread mới, chạy hàm sayHello()
std::thread t(sayHello);
// Chờ thread hoàn thành
t.join();
std::cout << "Main thread done.\n";
return 0;
}Output (có thể thay đổi thứ tự):
Hello from thread!
Main thread done.Thread Lifecycle
┌─────────────────────────────────────────────────────────────────┐
│ THREAD LIFECYCLE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ std::thread t(func); │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ CREATED │ ─── Thread bắt đầu chạy ngay lập tức │
│ └─────────────┘ │
│ │ │
│ ├─────────────────┬─────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ join() │ │ detach() │ │ (nothing)│ │
│ │ │ │ │ │ │ │
│ │ Block │ │ Fire & │ │ ❌ UB! │ │
│ │ & Wait │ │ Forget │ │ Crash! │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ DONE │ │ DETACHED │ │ std:: │ │
│ │ (safe) │ │ (runs │ │ terminate│ │
│ │ │ │ alone) │ │ called │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘⚠️ CRITICAL RULE
Một thread PHẢI được join() hoặc detach() trước khi destructor được gọi! Nếu không, chương trình sẽ gọi std::terminate() và crash.
join() vs detach()
join() — Chờ đợi
cpp
#include <iostream>
#include <thread>
#include <chrono>
void longTask() {
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Task completed!\n";
}
int main() {
std::thread t(longTask);
std::cout << "Waiting for thread...\n";
t.join(); // ⏳ Block cho đến khi thread xong
std::cout << "Thread finished, continuing...\n";
return 0;
}Output:
Waiting for thread...
Task completed! (sau 2 giây)
Thread finished, continuing...detach() — Fire and Forget
cpp
#include <iostream>
#include <thread>
#include <chrono>
void backgroundTask() {
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << "Background task done!\n"; // ⚠️ Có thể không in!
}
int main() {
std::thread t(backgroundTask);
t.detach(); // Thread chạy độc lập
std::cout << "Main continues immediately...\n";
// ⚠️ Nếu main() kết thúc trước thread, output sẽ bị cắt!
std::this_thread::sleep_for(std::chrono::seconds(4));
return 0;
}⚠️ Detach Caveats
- Thread detached không thể join lại
- Nếu main() kết thúc, detached threads bị force terminate
- Không thể biết khi nào thread hoàn thành
- Use case: Daemon threads, logging, monitoring
Truyền Arguments
By Value (Copy)
cpp
#include <iostream>
#include <thread>
#include <string>
void greet(std::string name, int times) {
for (int i = 0; i < times; ++i) {
std::cout << "Hello, " << name << "!\n";
}
}
int main() {
std::string myName = "PENALGO";
// Arguments được COPY vào thread
std::thread t(greet, myName, 3);
myName = "Changed"; // Không ảnh hưởng thread
t.join();
return 0;
}By Reference (với std::ref)
cpp
#include <iostream>
#include <thread>
#include <functional> // std::ref
void increment(int& value) {
value += 10;
}
int main() {
int counter = 0;
// ❌ SAI: Sẽ copy, không phải reference
// std::thread t1(increment, counter);
// ✅ ĐÚNG: Dùng std::ref() để truyền reference
std::thread t(increment, std::ref(counter));
t.join();
std::cout << "Counter: " << counter << std::endl; // 10
return 0;
}Passing Member Functions
cpp
#include <iostream>
#include <thread>
class Worker {
public:
void doWork(int id) {
std::cout << "Worker " << id << " is working...\n";
}
};
int main() {
Worker worker;
// Truyền member function: &Class::method, &object, args...
std::thread t(&Worker::doWork, &worker, 42);
t.join();
return 0;
}Lambda Functions
cpp
#include <iostream>
#include <thread>
int main() {
int localVar = 100;
// Lambda với capture
std::thread t([&localVar]() {
std::cout << "Lambda sees: " << localVar << std::endl;
localVar = 200;
});
t.join();
std::cout << "After thread: " << localVar << std::endl; // 200
return 0;
}std::this_thread Utilities
cpp
#include <iostream>
#include <thread>
#include <chrono>
void demoUtilities() {
// 1. Get thread ID
std::thread::id myId = std::this_thread::get_id();
std::cout << "Thread ID: " << myId << std::endl;
// 2. Sleep for duration
std::this_thread::sleep_for(std::chrono::milliseconds(500));
// 3. Sleep until specific time point
auto wakeTime = std::chrono::steady_clock::now()
+ std::chrono::seconds(1);
std::this_thread::sleep_until(wakeTime);
// 4. Yield — nhường CPU cho thread khác
std::this_thread::yield();
}
int main() {
std::cout << "Main thread ID: "
<< std::this_thread::get_id() << std::endl;
std::thread t(demoUtilities);
t.join();
return 0;
}Hardware Concurrency
cpp
#include <iostream>
#include <thread>
#include <vector>
int main() {
// Số lượng hardware threads (cores/hyperthreads)
unsigned int numCores = std::thread::hardware_concurrency();
std::cout << "Hardware threads: " << numCores << std::endl;
// Tạo đúng số threads tối ưu
std::vector<std::thread> threads;
for (unsigned int i = 0; i < numCores; ++i) {
threads.emplace_back([i]() {
std::cout << "Thread " << i << " on core\n";
});
}
for (auto& t : threads) {
t.join();
}
return 0;
}💡 Best Practice
Số threads tối ưu thường là hardware_concurrency() cho CPU-bound tasks, hoặc nhiều hơn cho I/O-bound tasks.
RAII Pattern với Thread
❌ Vấn đề: Exception Safety
cpp
void riskyCode() {
std::thread t(someFunction);
doSomething(); // ⚠️ Nếu throw exception...
t.join(); // ... dòng này không được gọi → CRASH!
}✅ Giải pháp: Custom RAII Wrapper
cpp
#include <thread>
#include <utility>
class ScopedThread {
std::thread t_;
public:
explicit ScopedThread(std::thread t)
: t_(std::move(t)) {
if (!t_.joinable()) {
throw std::logic_error("No thread");
}
}
~ScopedThread() {
t_.join(); // Tự động join khi ra khỏi scope
}
// Không cho copy
ScopedThread(const ScopedThread&) = delete;
ScopedThread& operator=(const ScopedThread&) = delete;
};
void safeCode() {
ScopedThread st(std::thread(someFunction));
doSomething(); // Dù có exception, thread vẫn được join!
}✅ C++20: std::jthread (Auto-joining Thread)
cpp
#include <thread>
#include <iostream>
void task() {
std::cout << "jthread task running\n";
}
int main() {
std::jthread jt(task); // C++20
// Không cần gọi join()!
// jthread tự động join trong destructor
return 0;
}Move Semantics với Thread
cpp
#include <thread>
#include <vector>
#include <iostream>
int main() {
std::thread t1([]() {
std::cout << "Thread 1\n";
});
// Threads không thể copy, chỉ có thể move
// std::thread t2 = t1; // ❌ Compile error
std::thread t2 = std::move(t1); // ✅ OK
// t1 giờ "empty", t2 owns the thread
std::cout << "t1 joinable: " << t1.joinable() << std::endl; // 0
std::cout << "t2 joinable: " << t2.joinable() << std::endl; // 1
t2.join();
// Container của threads
std::vector<std::thread> threads;
for (int i = 0; i < 3; ++i) {
threads.emplace_back([i]() {
std::cout << "Thread " << i << "\n";
});
}
for (auto& t : threads) {
t.join();
}
return 0;
}📚 Tổng kết
| Feature | Code |
|---|---|
| Tạo thread | std::thread t(func, args...); |
| Chờ hoàn thành | t.join(); |
| Chạy độc lập | t.detach(); |
| Kiểm tra joinable | t.joinable() |
| Get ID | std::this_thread::get_id() |
| Sleep | std::this_thread::sleep_for(duration) |
| Yield | std::this_thread::yield() |
| Hardware threads | std::thread::hardware_concurrency() |
| Pass by ref | std::ref(var) |
➡️ Tiếp theo
Bây giờ bạn biết cách tạo threads, hãy xem điều gì xảy ra khi nhiều threads truy cập cùng data...
Race Conditions → — Khi mọi thứ đổ vỡ.