10-C++线程相关
1. 核心库:<thread>
将可调用对象(函数、函数指针、Lambda 表达式、函数对象)传递给 std::thread 构造函数来创建线程。 创建即启动,一旦 std::thread 对象被构造,线程立即开始执行。主线程(主线程是相对的)调用某一线程上的join() 函数,来阻塞主线程,直到该线程线程执行完毕变为非活动无连接状态。 detach()函数将线程变为无连接状态,可将线程变为后台线程(“守护线程”)。分离后,将无法再控制该线程。必须在你销毁 std::thread 对象之前调用 join() 或 detach(),否则 std::terminate 会被调用,程序异常终止。
2. 互斥与锁:<mutex>
用于保护共享数据,防止数据竞争。互斥量类型 有最基本的互斥量std::mutex; 带超时功能的互斥量 std::timed_mutex ;允许同一线程多次加锁的可重入互斥量 std::recursive_mutex; 读写锁 std::shared_mutex 。
std::vector<std::thread> workers;
workers.emplace_back(worker);// 等价于 workers.push_back(std::thread(worker));
手动实现RAII管理锁
#include <mutex>
#include <iostream>
class MutexGuard {
private:std::mutex& mutex_;
public: // explicit是一个关键字,用于修饰类的构造函数,防止编译器进行隐式类型转换。explicit MutexGuard(std::mutex& mtx) : mutex_(mtx) {mutex_.lock(); // 构造函数获取资源(锁定mutex)std::cout << "Mutex locked" << std::endl;}~MutexGuard() {mutex_.unlock(); // 析构函数释放资源(解锁mutex)std::cout << "Mutex unlocked" << std::endl;}// 禁止拷贝构造和赋值操作MutexGuard(const MutexGuard&) = delete;MutexGuard& operator=(const MutexGuard&) = delete;
};
static std::mutex mux;
void Test() {MutexGuard lock(mux);// 业务代码// 函数执行完,执行lock的析构函数释放mutex
}
std::lock_guard 使用 RAII 管理锁的生命周期,在函数结束或异常时自动释放锁,从而避免死锁或忘记解锁的问题。 std::lock_guard<std::mutex> 变量名(std::mutes对象);
3. 条件变量:<condition_variable>
用于线程间的同步,允许线程阻塞并等待某个条件成立。 std::condition_variable:必须与 std::unique_lock<std::mutex> 配合使用。
- 核心操作:
wait():阻塞当前线程,直到被通知。notify_one():唤醒一个等待中的线程。notify_all():唤醒所有等待中的线程。
- 虚假唤醒:等待的线程可能在没有收到通知的情况下被唤醒。因此,
wait通常需要一个谓词(条件) 来循环检查。cv.wait(lock, []{ return condition; });是防止虚假唤醒的标准写法。
示例代码(生产者-消费者模型):
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>std::queue<int> data_queue;
std::mutex queue_mutex;
std::condition_variable queue_condvar;void producer() {for (int i = 0; i < 10; ++i) {std::this_thread::sleep_for(std::chrono::milliseconds(100));{std::lock_guard<std::mutex> lock(queue_mutex);data_queue.push(i);std::cout << "Produced: " << i << std::endl;}queue_condvar.notify_one(); // 通知一个消费者}
}void consumer() {while (true) {std::unique_lock<std::mutex> lock(queue_mutex);// 等待条件成立:队列非空。lambda 表达式是防止虚假唤醒的关键。queue_condvar.wait(lock, []{ return !data_queue.empty(); });int value = data_queue.front();data_queue.pop();lock.unlock(); // 尽早释放锁std::cout << "Consumed: " << value << std::endl;if (value == 9) break; // 简单终止条件}
}int main() {std::thread prod(producer);std::thread cons(consumer);prod.join();cons.join();return 0;
}
4. 异步操作:<future> 和 <async>
提供了一种更高层次的抽象来获取异步任务(在另一个线程中执行的函数)的结果。
std::async启动一个异步任务,返回一个 std::future 对象。 (std::future:提供了一种访问异步操作结果的机制。 get():获取结果。如果异步操作尚未完成,会阻塞当前线程直到完成。get() 只能调用一次。 wait():等待异步操作完成,但不获取结果。)
std::future<函数返回类型> 对象名= std::async(启动策略, 函数名, 参数1, 参数2, ...);
启动策略: std::launch::async 指立即在新线程中执行。 std::launch::deferred 指延迟执行,直到在 future 上调用 get() 或 wait() 时才在当前线程执行。
std::promise是 C++11 引入的一个同步原语,通常与 std::future 配合使用,用于在线程之间传递异步操作的结果。生产者线程可以通过promise.set_value()设置值,消费者线程通过关联的future.get()来获取。
#include <iostream>
#include <future>
#include <thread>
void setValue(std::promise<int> prom) { // future 与 函数关联std::this_thread::sleep_for(std::chrono::seconds(1));prom.set_value(42); // 设置结果值
}
int main() {std::promise<int> prom;std::future<int> fut = prom.get_future(); // 获取关联的futurestd::thread t(setValue, std::move(prom)); // future 与 函数对应的线程关联// 阻塞等待结果int result = fut.get();std::cout << "Result: " << result << std::endl; // 输出: Result: 42t.join();return 0;
}
5. 原子操作:<atomic>
提供了一种无需使用锁就能实现线程安全访问基本数据类型的方法。
关键知识:
std::atomic<T>:模板类,支持int,long,bool, 指针等类型。- 内存顺序:
std::memory_order允许你指定原子操作的内存同步约束(如memory_order_relaxed,memory_order_acquire,memory_order_release等),用于性能优化,但对初学者来说较复杂,通常使用默认的强顺序(memory_order_seq_cst)即可。 - 常用操作:
load(),store(),exchange(),compare_exchange_strong/weak等。
示例代码(无锁计数器):
#include <iostream>
#include <thread>
#include <vector>
#include <atomic>std::atomic<int> atomic_counter{0}; // 原子计数器void atomic_worker() {for (int i = 0; i < 10000; ++i) {++atomic_counter; // 此操作是原子的,无需加锁}
}int main() {std::vector<std::thread> workers;for (int i = 0; i < 4; ++i) {workers.emplace_back(atomic_worker);}for (auto &t : workers) {t.join();}std::cout << "Final atomic counter value: " << atomic_counter << std::endl; // 一定是 40000return 0;
}
-
thread_local用于解决多线程环境中需要每个线程拥有自己独立副本的变量问题(全局变量和静态变量被所有线程共享)。它提供了一种声明线程局部存储(TLS)的方式,即每个线程都有该变量的独立实例,且在线程的生命周期内存在。 -
std::chrono是 C++ 标准库中用于处理时间和日期的库,它提供了一系列类型和函数来进行时间点、时长和时钟的操作。 -
std::this_thread是控制当前正在执行这段代码的线程。
std::this_thread::sleep_for(std::chrono::seconds(2));
