C++ 的 std::thread
库是 C++11 引入的一个标准库,用于创建和管理线程。线程允许程序并发地执行代码,即使在同一个处理器上也可以通过时间切片的方式进行并发。多线程编程能提升应用程序的响应性和性能,特别是在多核处理器上。
1. std::thread 基本用法
创建线程
C++ 中可以使用 std::thread
类创建并启动线程,每个线程在后台独立执行传入的任务。线程的任务可以是函数、成员函数或 Lambda 表达式。
#include <iostream>
#include <thread>
void print_message() {
std::cout << "Hello from thread!" << std::endl;
}
int main() {
std::thread t(print_message); // 创建一个新线程执行 print_message 函数
t.join(); // 等待线程 t 完成
return 0;
}
其中,线程 t
执行 print_message
函数。当 t.join()
被调用时,主线程会等待子线程 t
执行完成后再继续执行。
2. Lambda 表达式作为线程任务
可以使用 Lambda 表达式来作为线程的任务,使代码更加简洁。
#include <iostream>
#include <thread>
int main() {
std::thread t([]{
std::cout << "Hello from Lambda thread!" << std::endl;
});
t.join(); // 等待线程完成
return 0;
}
3. 线程与参数传递
可以将参数传递给线程函数,包括按值传递和按引用传递。
按值传递
默认情况下,线程会将传入的参数按值传递给线程函数。
#include <iostream>
#include <thread>
void print_number(int n) {
std::cout << "Number: " << n << std::endl;
}
int main() {
int x = 10;
std::thread t(print_number, x); // 按值传递
t.join();
return 0;
}
按引用传递
如果希望按引用传递参数,需要使用 std::ref
将变量包裹。
#include <iostream>
#include <thread>
void increment(int& n) {
++n;
}
int main() {
int x = 10;
std::thread t(increment, std::ref(x)); // 按引用传递 x
t.join();
std::cout << "x after thread: " << x << std::endl; // 输出 11
return 0;
}
4. 库函数 join() 和 detach()
join()
join()
会阻塞主线程,直到子线程完成。这确保主线程等待所有工作线程结束后再继续执行。若没有调用 join()
,主线程可能会在子线程完成之前结束。
std::thread t(task);
t.join(); // 等待 t 完成
detach()
detach()
会将线程与当前线程分离,使其在后台独立运行,主线程无需等待它完成。调用 detach()
后,线程在后台执行完成后自动释放资源。此方法适合那些无需等待其结束的任务。
std::thread t(task);
t.detach(); // t 在线程后台独立执行
注意: detach()
后主线程不会再追踪该线程,不能再 join()
已分离的线程,线程结束后资源自动释放,可能会导致资源泄漏或访问非法内存等问题,因此要谨慎使用。
5. std::thread 的常用方法
以下是 std::thread
类中的一些常用方法:
join()
阻塞当前线程,直到调用 join()
的线程完成。
detach()
使线程在后台独立运行,不与主线程同步。
joinable()
判断线程是否可以被 join()
。如果线程已经 join()
或 detach()
,则 joinable()
返回 false
。
std::thread t(task);
if (t.joinable()) {
t.join();
}
get_id()
返回线程的 ID,作为 std::thread::id
类型。如果线程还没有启动或者已经完成,ID 可能是一个特殊值。
std::thread t(task);
std::cout << "Thread ID: " << t.get_id() << std::endl;
hardware_concurrency()
返回系统中可能的并发线程数,即 CPU 核心数。这是一个静态方法,可以用于确定程序中应使用多少个线程。
unsigned int cores = std::thread::hardware_concurrency();
std::cout << "Number of cores: " << cores << std::endl;
6. 线程同步
多线程编程中,线程之间共享资源可能会引发数据竞争。为了防止这种情况,需要使用同步机制。C++ 提供了多种线程同步工具,包括互斥锁(std::mutex
)、条件变量(std::condition_variable
)、锁定器(std::lock_guard
)等。
互斥锁 std::mutex
std::mutex
是一种互斥锁,可以防止多个线程同时访问共享数据。
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
void print_message(const std::string& message) {
std::lock_guard<std::mutex> lock(mtx); // 自动加锁与解锁
std::cout << message << std::endl;
}
int main() {
std::thread t1(print_message, "Thread 1: Hello");
std::thread t2(print_message, "Thread 2: World");
t1.join();
t2.join();
return 0;
}
std::lock_guard<std::mutex>
是一个封装类,它会自动管理互斥锁的锁定和解锁,避免手动调用lock()
和unlock()
。
条件变量 std::condition_variable
条件变量允许线程等待某个条件发生,并在条件满足时通知其他线程。这在生产者-消费者模型中非常有用。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void wait_for_signal() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return ready; });
std::cout << "Thread received signal!" << std::endl;
}
void send_signal() {
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one(); // 通知等待的线程
}
int main() {
std::thread t1(wait_for_signal);
std::thread t2(send_signal);
t1.join();
t2.join();
return 0;
}
cv.wait()
会阻塞线程,直到接收到信号(即notify_one()
或notify_all()
被调用),并且条件满足。
7. std::future 和 std::async
std::future
和 std::async
提供了一种更高层次的线程处理方式,它允许启动异步任务并获取任务的结果,而无需手动管理线程。
#include <iostream>
#include <future>
int compute_sum(int a, int b) {
return a + b;
}
int main() {
std::future<int> result = std::async(compute_sum, 10, 20); // 异步任务
std::cout << "Result: " << result.get() << std::endl; // 获取结果
return 0;
}
std::async
启动一个异步任务,并返回std::future
对象,通过调用get()
来获取异步任务的结果。
8. 线程池(Thread Pool)
在实际应用中,线程池是一种常见的优化策略,它避免了反复创建和销毁线程的开销。C++ 标准库没有内建的线程池实现,但可以自己实现一个简单的线程池,或使用第三方库(如 Boost
)。
一个简化版的线程池例子:
#include <iostream>
#include <vector>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
class ThreadPool {
public:
ThreadPool(size_t numThreads);
~ThreadPool();
void enqueue(std::function<void()> task);
private:
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
std::mutex queueMutex;
std::condition_variable condition;
bool stop;
void worker();
};
ThreadPool::ThreadPool(size_t numThreads) : stop(false) {
for (size_t i = 0; i < numThreads; ++i) {
workers.emplace_back(&ThreadPool::worker, this);
}
}
ThreadPool::~