1C++11多线程
主要对少用的知识点进行详细描述,用的多的就给示例。照猫画虎
#include<thread>
// 默认构造函数
thread()
// 初始化构造函数
template<class Fn, class... Args>
explicit thread(Fn&& fn, Args&&... args)
// 移动构造函数
thread(thread&& x) noexcept
//主要API:
// 获取线程ID
get_id()
// 判断线程是否可join
joinable()
// 等待线程执行完成
join()
// 分离线程
detach()
// 创建类成员函数线程
class A {
public:
static void fun3(int a) {
cout << a << endl;
}
};
std::thread t3(&A::fun3, 1);
互斥量
C++11提供如下4种语义的互斥量(mutex)和RAII(通过类的构造析构)来实现更好的编码方式。
std::mutex,独占的互斥量,不能递归使用。
// 特点 - 最基本的互斥量 - 不支持递归锁定 - 同一线程重复锁定会导致死锁 // 主要方法 void lock(); // 加锁 void unlock(); // 解锁 bool try_lock(); // 尝试加锁
std::time_mutex,带超时的独占互斥量,不能递归使用。
// 特点 - 支持超时锁定 - 不支持递归锁定 // 额外方法 bool try_lock_for(duration); // 在指定时间内尝试锁定 bool try_lock_until(时间点); // 在指定时间点前尝试锁定
std::recursive_mutex,递归互斥量,不带超时功能。
// 特点 - 允许同一线程多次获取锁 - 需要相应次数的解锁 - 不带超时功能 // 使用场景 - 递归调用需要多次加锁的场景 - 不建议过度使用
std::recursive_timed_mutex,带超时的递归互斥量。
// 特点 - 结合递归锁和超时特性 - 同一线程可多次获取锁 - 支持超时锁定 // 方法 bool try_lock_for(duration); bool try_lock_until(时间点);
选择建议:
- 优先使用std::mutex
- 需要超时机制时使用std::timed_mutex
- 尽量避免递归锁
- 复杂场景可考虑std::recursive_timed_mutex
lock_guard和unique_lock的使用和区别
- 选择建议
- 简单场景:使用lock_guard
- 复杂场景:使用unique_lock
- 需要更精细锁控制:推荐unique_lock
lock_guard:
- 自动管理
- 不可手动控制
- 轻量级
- 简单安全
unique_lock:
- 可手动控制
- 支持延迟锁定
- 可转移所有权
- 更灵活
条件变量
// 作用
- 线程间同步机制
- 允许线程等待特定条件
- 比轮询更高效
// 主要类
std::condition_variable
std::condition_variable_any
核心方法
// 等待方法 1释放 2谓语判断 3挂起等唤醒 4.唤醒竞争锁。
void wait(unique_lock<mutex>& lock);
// 带谓词的等待
template<class Predicate>
void wait(unique_lock<mutex>& lock, Predicate pred);
//谓语参数2 等待 时间点 返回true
void wait_until(unique_lock<mutex>& lock,const time_point& absolute_time);
//谓语参数2 等待 时间段 返回true
void wait_for(unique_lock<mutex>& lock,const duration& relative_time);
// 通知方法
void notify_one(); // 唤醒一个等待线程
void notify_all(); // 唤醒所有等待线程
注意事项
- 总是使用unique_lock
- 防止虚假唤醒(使用谓词)
- 注意死锁风险
- 适当处理异常
使用建议
- 替代轮询
- 提高线程同步效率
- 配合互斥量使用
- 谨慎设计等待条件
原子变量
#include <atomic>
// 基本原子类型
std::atomic<int> atomic_int;
std::atomic<bool> atomic_bool;
std::atomic<long> atomic_long;
// 内存序类型
enum memory_order {
memory_order_relaxed, // 最宽松
memory_order_consume,
memory_order_acquire, // 获取语义
memory_order_release, // 释放语义
memory_order_acq_rel, // 获取释放
memory_order_seq_cst // 顺序一致性(默认)
};
核心操作:
// 基本操作
void store(T value); // 存储
T load() const; // 加载
T exchange(T value); // 替换并返回旧值
// 比较交换
bool compare_exchange_weak(T& expected, T desired);
bool compare_exchange_strong(T& expected, T desired);
关键点总结:
- 提供无锁线程安全操作
- 开销比互斥量低
- 适合简单类型
- 灵活的内存序控制
- 主要用于轻量同步
选择建议:
- 简单同步:原子变量
- 复杂同步:互斥量
- 性能关键:无锁编程
call_once 和 once_flag
#include <mutex>
#include <thread>
std::once_flag flag;
void init_function() {
// 仅执行一次的初始化代码 多线程安全
std::call_once(flag, [](){
// 初始化逻辑
std::cout << "Initialization" << std::endl;
});
}
void thread_func() {
init_function();
}
关键点总结:
- 线程安全的一次性初始化
- 避免重复初始化
- 支持异常处理
- 性能开销低
- 适合单例、资源初始化
使用建议:
- 替代传统的加锁初始化
- 简化多线程初始化逻辑
- 适用于静态初始化
- 处理复杂初始化场景
2 function和bind用法
关键点总结:
- std::function:类型擦除的可调用对象包装器
- std::bind:参数绑定和函数适配
- 支持多种可调用对象
- 提供灵活的回调机制
- 有一定性能开销
-
class FunctionPerformance { public: // 性能注意事项 void performanceConsiderations() { // 尽量避免频繁创建function对象 // 推荐: std::function<int(int)> cached_func = [](int x) { return x * x; }; // 不推荐:每次都创建新的function对象 for (int i = 0; i < 1000; ++i) { std::function<int(int)> temp_func = [](int x) { return x * x; }; // 性能开销较大 } } // 类型擦除的开销 void typeErasureOverhead() { // function会有一定的性能开销 // 对于性能敏感场景,考虑直接使用模板 // 开销较大的方式 std::function<int(int)> func = [](int x) { return x * x; }; // 轻量级模板方式 auto template_func = [](int x) { return x * x; }; } };
最佳实践:
- 适度使用,注意性能
- 优先使用模板
- 谨慎处理生命周期
- 避免过度复杂的绑定
使用建议:
- 简单场景:直接使用lambda
- 复杂参数:使用bind
- 类型无关:使用function
- 性能敏感:使用模板
3 可变模板参数
允许定义接受任意数量和类型参数的模板函数和模板类。
基本语法
template<typename... Args>
可变参数函数
// 打印所有参数
template<typename... Args>
void print(Args... args) {
(std::cout << ... << args) << std::endl;
}
// 使用
print(1, 2, 3, "hello"); // 可以接受不同类型和数量的参数
template<typename... Args>
void forwardAll(Args&&... args) {
// 完美转发所有参数
someFunction(std::forward<Args>(args)...);
}
// 基础情况
void print() {
std::cout << std::endl;
}
// 递归展开
template<typename T, typename... Args>
void print(T first, Args... rest) {
std::cout << first << " ";
print(rest...); // 递归调用
}
折叠表达式(C++17)
template<typename... Args>
auto sum(Args... args) {
return (args + ...); // 从左到右求和
}
// 使用示例
int result = sum(1, 2, 3, 4, 5); // 结果为15
简单的编译期求和
template<typename... Args>
constexpr auto compile_sum(Args... args) {
return (args + ...);
}
int main() {
constexpr int result = compile_sum(1, 2, 3, 4);
static_assert(result == 10, "Compile-time sum failed");
return 0;
}
注意事项:
- 可变模板参数需要小心处理,通常使用递归或折叠表达式展开
- C++17的折叠表达式大大简化了参数包的处理
- 可以与其他模板技术结合,如完美转发、类型萃取等
4 异步操作
#include <future>
#include <thread>
// 四者关系
// promise -> future (异步传递结果)
// async -> future (简化异步操作)
// packaged_task -> future (可调用对象的异步封装)
// 推荐:
- 使用async简化异步编程
- 合理处理异常
- 注意future的生命周期
- 避免过度创建线程
// 不推荐:
- 频繁创建future
- 忽略异常处理
- 长时间阻塞get()
-
std::future:
std::future 是一个容器类,它保存了一个异步操作的结果。
它提供了一种访问异步操作结果的方式,可以等待操作完成或检查其是否就绪。接收return值
std::future 可以从 std::promise ,async std::packaged_task 获得。 -
常用方法: get(): 等待异步任务完成并返回结果。 wait(): 等待异步任务完成,但不获取结果。 valid(): 检查 std::future 对象是否有效。 wait_for(timeout): 等待指定时间内异步任务完成。
-
#include <iostream> #include <future> #include <thread> #include <chrono> int asyncFunction() { std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时操作 return 42; } int main() { std::future<decltype (asyncFunction())> result = std::async(std::launch::async, asyncFunction); std::cout << "Doing other work..." << std::endl; // 这里可以执行其他工作,asyncFunction()会在后台执行 int value = result.get(); // 获取结果,如果操作尚未完成,这里会阻塞 std::cout << "The answer is " << value << std::endl; return 0; }
-
std::async
:std::async
是一个函数,它启动一个异步操作并返回一个std::future
来保存结果。- 它提供了一种简单的方式来异步执行一个函数并获取其结果。
std::async
可以使用不同的启动策略,-
policy: 一个 std::launch 类型的枚举值,用于指定函数的启动策略。可选值包括 std::launch::defered表明该函数会被延迟调用,直到在future上调用get()或者wait()为止 std::launch::async,表明函数会新创线程调用 f: 要异步执行的函数对象。可以是普通函数、lambda 表达式或成员函数。 args: 要传递给函数 f 的参数。 template <class Function, class... Args> [[nodiscard]] std::future<std::invoke_result_t<std::decay_t<Function>, std::decay_t<Args>...>> async(std::launch policy, Function&& f, Args&&... args); MyClass obj; auto result3 = std::async(std::launch::async, &MyClass::doSomething, &obj, 5, 10);
-
-
std::promise
:std::promise
是一个容器类,它保存了一个可以异步设置的值。- 它用于向
std::future
对象提供一个值,通常来自另一个线程或异步操作。 std::promise
通常与std::future
一起使用,实现并发编程中的生产者-消费者模式。void promise_example() { // promise用于在线程间传递单一值 std::promise<int> promise; std::future<int> future = promise.get_future(); // 在另一个线程中设置值 std::thread t([&promise](){ try { // 设置值 promise.set_value(42); // 或设置异常 // promise.set_exception(std::current_exception()); } catch(...) { promise.set_exception(std::current_exception()); } }); // 主线程获取值 try { int value = future.get(); std::cout << "Received: " << value << std::endl; } catch (const std::exception& e) { std::cout << "Exception: " << e.what() << std::endl; } t.join(); }
-
std::packaged_task
:std::packaged_task
是一个包装可调用对象(如函数或 lambda 表达式)的类,可以异步执行该对象。- 它提供了一种将
std::future
与异步操作的结果关联起来的方式。 std::packaged_task
在你想异步执行一个函数并获取其结果,但不想手动创建和管理std::promise
和std::future
时很有用。-
void packaged_task_example() { // 将可调用对象包装为异步任务 std::packaged_task<int(int,int)> task([](int a, int b) { return a + b; }); // 获取future std::future<int> future = task.get_future(); // 方法1:直接调用 task(10, 20); std::cout << "Result: " << future.get() << std::endl; // 方法2:在线程中执行 std::thread t(std::move(task), 30, 40); t.join(); }
关键特点:
- future:获取异步结果
- promise:设置异步结果
- async:简化异步任务
- packaged_task:可调用对象异步包装
选择建议:
- 简单异步:std::async
- 复杂控制:std::promise + std::future
- 可调用对象:std::packaged_task
综合示例简单的线程池
#include <iostream>
#include <future>
#include <queue>
#include <thread>
#include <vector>
#include <functional>
#include <condition_variable>
class ThreadPool {
private:
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
std::mutex queue_mutex;
std::condition_variable condition;
bool stop = false;
public:
ThreadPool(size_t threads) {
for (size_t i = 0; i < threads; ++i) {
workers.emplace_back([this] {
while (true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(queue_mutex);
condition.wait(lock, [this] {
return stop || !tasks.empty();
});
if (stop && tasks.empty()) return;
task = std::move(tasks.front());
tasks.pop();
}
task();
}
});
}
}
template<class F, class... Args>
auto enqueue(F&& task, Args&&... args)
-> std::future<std::invoke_result_t<F, Args...>>
{
using return_type = std::invoke_result_t<F, Args...>;
// 使用 std::packaged_task 包装任务
auto packaged_task =
std::make_shared<std::packaged_task<return_type()>>(
std::bind(std::forward<F>(task), std::forward<Args>(args)...)
);
std::future<return_type> future = packaged_task->get_future();
{
std::unique_lock<std::mutex> lock(queue_mutex);
tasks.emplace([packaged_task]() {
(*packaged_task)();
});
}
condition.notify_one();
return future;
}
~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queue_mutex);
stop = true;
}
condition.notify_all();
for (auto& worker : workers) {
worker.join();
}
}
};
// 使用示例
void async_thread_pool_demo() {
ThreadPool pool(4);
// 测试不同类型的任务
auto future1 = pool.enqueue([]() {
return 1;
});
auto future2 = pool.enqueue([](int x) {
return x + 2;
}, 3);
auto future3 = pool.enqueue([]() {
return std::string("hello");
});
// 多种类型的结果获取
std::cout << future1.get() << std::endl; // 输出 1
std::cout << future2.get() << std::endl; // 输出 5
std::cout << future3.get() << std::endl; // 输出 hello
}
int main() {
async_thread_pool_demo();
return 0;
}
学习资料分享