inux 基础入门操作 第十章 C++多线程介绍 2
1 C++ 线程介绍
std::thread 是 C++11 标准引入的线程库,提供面向对象的多线程编程接口,比传统的 POSIX 线程(pthread)更易用且类型安全。
1.1 基本用法
- 头文件与基本创建
#include <thread>
#include <iostream>
void hello() {
std::cout << "Hello from thread!\n";
}
int main() {
// 创建并启动线程
std::thread t(hello);
// 等待线程结束
t.join();
return 0;
}
- 带参数的线程函数
void print_num(int num, const std::string& str) {
std::cout << "Number: " << num << " String: " << str << "\n";
}
int main() {
std::thread t(print_num, 42, "Answer");
t.join();
}
1.2 常见的api函数
1.3 detach() 介绍
- 所有权转移:
调用detach()后,std::thread对象不再拥有该线程。线程变为"守护线程"(daemon thread),在后台运行
- 资源管理:
分离的线程结束时,系统会自动回收其资源。不需要也不能再调用join()。
- 调用限制:
只能在joinable()为true时调用(即新创建且未join/detach过的线程)。调用后joinable()变为false
detach()是一个强大但需要谨慎使用的工具,正确使用可以实现有效的后台任务处理,但不当使用会导致难以调试的问题。在大多数情况下,优先考虑使用join()或更高级的抽象(如std::async或线程池)。
1.3.1 案例
void log_writer() {
while(true) {
write_logs();
std::this_thread::sleep_for(std::chrono::minutes(1));
}
}
int main() {
std::thread t(log_writer);
t.detach();
// ... 主程序继续
}
2 同步设置
2.1 互斥锁
互斥锁是多线程编程中最基础的同步机制,用于保护共享数据,防止多个线程同时访问造成的数据竞争问题。C++11在标准库中引入了多种互斥锁类型。
2.1.1 一般应用
#include <mutex>
#include <thread>
std::mutex mtx; // 全局互斥锁
int shared_data = 0;
void increment() {
mtx.lock(); // 加锁
++shared_data; // 临界区
mtx.unlock(); // 解锁
}
2.1.2 更加安全应用
- std::lock_guard 最简单的RAII锁管理,构造时加锁,析构时解锁
{
std::lock_guard<std::mutex> lock(mtx); // 进入作用域加锁
// 临界区代码
} // 离开作用域自动解锁
- std::unique_lock 更灵活的RAII锁管理,支持延迟锁定、条件变量等。
std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 延迟加锁
if(lock.try_lock()) { // 手动尝试加锁
// 临界区代码
}
案例如下
#include <mutex>
std::mutex mtx;
int shared_data = 0;
void safe_increment() {
std::lock_guard<std::mutex> lock(mtx);
++shared_data;
}
2.2 条件变量
条件变量是多线程编程中用于线程间同步的重要机制,它允许线程在某个条件不满足时挂起,直到其他线程通知条件可能已发生变化。
条件变量总是与互斥锁配合使用,主要解决以下问题:
-
线程需要等待某个条件成立
-
避免忙等待(busy-waiting)造成的CPU资源浪费
-
实现高效的线程间通信
2.2.1 基本组件
#include <condition_variable>
std::mutex mtx; // 互斥锁
std::condition_variable cv; // 条件变量
bool ready = false; // 共享条件
2.2.2 等待
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; }); // 条件不满足时自动释放锁并等待
// 条件满足后继续执行,锁已重新获取
运行案例:
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void worker() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; });
// 处理数据
}
void controller() {
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_all();
}
3 与pthread的区别
4 问题
4.1 资源泄漏风险
void risky_function() {
std::thread t(heavy_task);
if(some_condition) {
throw std::runtime_error("Oops");
// 线程t未被join,导致资源泄漏
}
t.join();
}
4.2 过渡创建
for(int i=0; i<1000; ++i) {
std::thread t(short_task); // 频繁创建/销毁线程代价高
t.detach();
}
5 线程池
线程池是一种多线程处理形式,它预先创建一组线程并维护这些线程的生命周期,通过任务队列机制来处理并发任务。线程池是现代并发编程的基础设施,合理使用可以显著提升程序性能,同时降低系统资源消耗。开发者应根据具体场景选择合适的线程池实现和配置参数。
// 使用线程池示例(伪代码)
ThreadPool pool(4); // 4个工作线程
for(auto& task : tasks) {
pool.enqueue(task); // 任务排队执行
}