diff --git a/Thread/Async-Future.md b/Thread/Async-Future.md new file mode 100644 index 0000000..836e341 --- /dev/null +++ b/Thread/Async-Future.md @@ -0,0 +1,190 @@ +哈哈,好的,你现在已经把 **线程、互斥、原子**都啃完了,剩下的就是 **“有返回值的并发”**——也就是 `` / `` 的领域了。 +我给你整理一份 **速查表+说明**,保证看完就能用。 + +--- + +# 一、`std::async` 基本概念 + +> **作用**:开启一个任务,并且自动管理线程,可以**拿返回值**。 +> 它本质是“带未来值的线程”。 + +### 1️⃣ 基本用法 + +```cpp +#include +#include +using namespace std; + +int work(int x) { return x * x; } + +int main() { + future f = async(work, 5); + int result = f.get(); // 阻塞直到返回值就绪 + cout << result << endl; // 25 +} +``` + +### 参数详解 + +```cpp +async(launch::policy policy, Callable f, Args... args) +``` + +* `policy` 可选: + + * `launch::async` → 必在新线程运行 + * `launch::deferred` → 等到调用 `get()` 才执行(懒加载) + * 可以用 `|` 混合 +* `f` → 函数或 lambda +* `args...` → 参数 + +### 返回值 + +* `std::future` +* `get()` 得到实际返回值 +* 阻塞直到任务完成 +* 调用一次 `get()` 后 future 失效 + +--- + +# 二、`std::future` 基本操作 + +| 函数 | 作用 | 返回值 | +| ------------------------ | -------- | --------------- | +| `get()` | 阻塞等待结果 | `R` | +| `wait()` | 阻塞直到准备好 | void | +| `wait_for(duration)` | 阻塞指定时间 | `future_status` | +| `wait_until(time_point)` | 阻塞到指定时间点 | `future_status` | +| `valid()` | 是否有有效任务 | bool | + +`future_status` 枚举: + +```cpp +std::future_status::ready // 已就绪 +std::future_status::timeout // 超时 +std::future_status::deferred // 延迟执行 +``` + +--- + +# 三、`std::promise`(future 的搭档) + +> **作用**:手动给 future 设置值 + +```cpp +#include + +std::promise p; +std::future f = p.get_future(); + +// 子线程 +std::thread([&p](){ + p.set_value(42); +}).detach(); + +// 主线程 +int val = f.get(); // 42 +``` + +### 关键点 + +* `set_value()` → 给 future 赋值 +* `set_exception()` → 把异常传给 future +* `get_future()` → 得到与 promise 绑定的 future + +--- + +# 四、组合 async + future + lambda(最常用) + +```cpp +auto f = async([](int x){ return x*2; }, 10); +cout << f.get(); // 20 +``` + +* 不用手动管理线程 +* 可以拿返回值 +* 自动 join / 管理 + +--- + +# 五、等待与超时(高级) + +```cpp +future f = async(work, 5); +if(f.wait_for(500ms) == future_status::ready) { + cout << f.get() << endl; +} else { + cout << "还没完成" << endl; +} +``` + +* 非阻塞方式获取结果 +* 用于并行计算 + 超时控制 + +--- + +# 六、`future` 全家桶速查表 + +| 类型 / 函数 | 作用 | 返回值 | +| ------------------------ | ------------- | ------------- | +| `std::future` | 保存异步任务结果 | — | +| `get()` | 阻塞获取结果 | `T` | +| `wait()` | 阻塞直到就绪 | void | +| `wait_for(duration)` | 阻塞指定时间 | future_status | +| `wait_until(time_point)` | 阻塞到指定时间点 | future_status | +| `valid()` | 是否有效 | bool | +| `std::promise` | 手动设置 future 值 | — | +| `set_value()` | 设置值 | void | +| `set_exception()` | 设置异常 | void | +| `get_future()` | 获取 future | future | +| `std::async()` | 开启任务 | future | + +--- + +# 七、使用建议 + +1️⃣ **如果需要返回值 → 用 async 或 promise/future** +2️⃣ **不用 async 就直接 thread + mutex/atomic → 仅做“干活”** +3️⃣ **async + lambda 最简洁**,避免手动 join / detach +4️⃣ **多线程大计算 → future + async + wait_for**,保证不会死锁 + +--- + +# 八、示例:多线程计算 + 获取返回值 + +```cpp +#include +#include +#include +using namespace std; + +int work(int x){ return x*x; } + +int main(){ + vector> tasks; + for(int i=1; i<=4; i++){ + tasks.push_back(async(work, i)); + } + for(auto &f: tasks){ + cout << f.get() << " "; + } + cout << endl; +} +``` + +输出:`1 4 9 16`(顺序可能一样也可能不同) + +--- + +总结一句话: + +> **thread → 干活** +> **mutex/atomic → 防抢/快抢** +> **future/async → 干活 + 拿结果** +> **promise → 未来值的手动操控** + +--- + +我可以帮你 **把之前的“拷贝大文件”项目升级成 async + future 版本**,实现 **多线程拷贝 + 返回每个块的字节数 + 进度统计**,让你真正把线程+future全套用起来。 + +你想试吗? diff --git a/Thread/Atomic.md b/Thread/Atomic.md new file mode 100644 index 0000000..2e2843f --- /dev/null +++ b/Thread/Atomic.md @@ -0,0 +1,319 @@ +哈哈,**行,这套并发三件套我们直接收官** 😄 +你已经把 ``、`` 啃完了,现在这个 `` **是精华也是雷区**。 +我给你一份**能当“原子操作说明书”的完整速查版**。 + +--- + +# 一、`` 是干嘛的?一句话版 + +> **`` 用来做“无锁但安全”的共享小数据。** + +关键词只有三个: + +* 小 +* 简单 +* 高频 + +典型:计数器、标志位、状态机。 + +--- + +# 二、`std::atomic` 基础 + +## 1️⃣ 定义原子变量 + +```cpp +#include + +std::atomic x{0}; +``` + +### 支持的常见类型 + +* `int / bool / long` +* 指针 +* 枚举 +* trivially copyable 类型(有限) + +--- + +## 2️⃣ 读 / 写(load / store) + +```cpp +x.store(10); +int v = x.load(); +``` + +### 默认内存序 + +* `memory_order_seq_cst`(最安全、最慢) + +### 返回值 + +* `store`:❌ 无 +* `load`:✔ 返回值 + +--- + +## 3️⃣ 原子赋值(像普通变量一样) + +```cpp +x = 5; +int y = x; +``` + +✔ 语法糖 +✔ 等价于 `store / load` + +--- + +# 三、原子读改写(重点) + +## 4️⃣ `fetch_add` / `fetch_sub` + +```cpp +int old = x.fetch_add(1); +``` + +### 语义 + +* 返回 **修改前的值** +* 然后再加 + +```cpp +x++; // 等价 fetch_add(1) +--x; // fetch_sub(1) +``` + +### 返回值 + +✔ 修改前的值 + +--- + +## 5️⃣ `exchange()` —— 原子替换 + +```cpp +int old = x.exchange(42); +``` + +### 作用 + +* 原子地: + + * old ← x + * x ← 42 + +### 返回值 + +✔ 旧值 + +--- + +## 6️⃣ `compare_exchange_weak / strong`(最难) + +### 用途 + +> **CAS(Compare And Swap)** + +```cpp +int expected = 10; +x.compare_exchange_strong(expected, 20); +``` + +### 行为 + +* 如果 `x == expected` + + * `x = 20` + * 返回 `true` +* 否则 + + * `expected = x` + * 返回 `false` + +### 返回值 + +✔ `bool` + +### weak vs strong + +| 版本 | 特点 | +| -------- | ---------- | +| `weak` | 可能失败(用于循环) | +| `strong` | 不会伪失败 | + +--- + +# 四、指针原子(很实用) + +## 7️⃣ `std::atomic` + +```cpp +std::atomic p; +``` + +### 常用操作 + +```cpp +Node* old = p.load(); +p.store(new_node); +``` + +```cpp +p.compare_exchange_strong(old, new_node); +``` + +👉 无锁链表、对象切换神器 + +--- + +# 五、内存序(进阶但你必须知道) + +> **默认你可以全用 `seq_cst`,不会错。** + +### 常见枚举 + +```cpp +std::memory_order_relaxed +std::memory_order_acquire +std::memory_order_release +std::memory_order_acq_rel +std::memory_order_seq_cst +``` + +### 什么时候用? + +| 场景 | 内存序 | +| --------- | ----------------- | +| 简单计数 | relaxed | +| 生产者 / 消费者 | acquire / release | +| 不想翻车 | seq_cst | + +--- + +## 示例(生产者-消费者) + +```cpp +data = 42; +flag.store(true, memory_order_release); +``` + +```cpp +while (!flag.load(memory_order_acquire)); +use(data); +``` + +--- + +# 六、原子标志(最轻量) + +## 8️⃣ `std::atomic_flag` + +```cpp +std::atomic_flag f = ATOMIC_FLAG_INIT; +``` + +### 操作 + +```cpp +f.test_and_set(); // 返回旧值 +f.clear(); +``` + +✔ 自旋锁 +✔ 最底层 + +--- + +# 七、等待 / 通知(C++20) + +## 9️⃣ `wait / notify_one / notify_all` + +```cpp +x.wait(0); +x.store(1); +x.notify_one(); +``` + +### 作用 + +* 原子级别的条件变量 +* 无 mutex + +### 返回值 + +❌ 无 + +--- + +# 八、完整 API 总表(收藏) + +| API | 作用 | 返回值 | +| --------------------------- | ------- | ---- | +| `atomic` | 原子变量 | — | +| `load()` | 原子读 | T | +| `store()` | 原子写 | void | +| `exchange()` | 替换 | T | +| `fetch_add()` | 加 | T | +| `fetch_sub()` | 减 | T | +| `compare_exchange_weak()` | CAS | bool | +| `compare_exchange_strong()` | CAS | bool | +| `atomic_flag` | 最小原子 | — | +| `test_and_set()` | 设置并返回旧值 | bool | +| `clear()` | 清空 | void | +| `wait()` | 等待 | void | +| `notify_one()` | 唤醒一个 | void | +| `notify_all()` | 唤醒全部 | void | + +--- + +# 九、什么时候用 atomic?一句判断法 + +> **如果共享数据:** +> +> * 类型简单 +> * 操作简单 +> * 不涉及多个变量一致性 + +👉 **atomic** + +否则: + +👉 **mutex** + +--- + +# 🔥 十、新手 atomic 四大雷区(务必避开) + +1️⃣ **以为 atomic 能解决一切**(不能) +2️⃣ **多个 atomic 组合 ≠ 原子整体** +3️⃣ **乱用 relaxed** +4️⃣ **atomic + 普通变量混用无同步** + +--- + +# 十一、你现在的并发地图已经完整了 + +```text +thread → 干活 +mutex → 防抢 +atomic → 快而小 +future → 拿结果 +``` + +你现在完全可以: + +* 看懂并发代码 +* 写不翻车的并发 +* 判断“该不该多线程” + +--- + +如果你愿意,**最后一关**我可以带你做: + +* 🔥 一个 **无锁队列(atomic + CAS)** +* 🔥 或把你之前那个拷贝工具 **升级成 atomic 进度条** +* 🔥 或讲 **C++ 内存模型白话终极版** + +你点哪个,我收官。 diff --git a/Thread/Mutex.md b/Thread/Mutex.md new file mode 100644 index 0000000..e043d2b --- /dev/null +++ b/Thread/Mutex.md @@ -0,0 +1,304 @@ +哈哈,**好,这一波咱把 `` 彻底端了** 😄 +你现在这个节奏是对的:**thread 看清 → mutex 看透 → 并发就不怕了**。 + +我给你的是一份 **能当“并发速查表”的 `` 全家桶说明**,照着用不翻车。 + +--- + +# 一、`` 是干嘛的?一句话版 + +> **`` 负责“同步”,不负责“并发”。** + +* `thread`:开人 +* `mutex`:防抢 +* `atomic`:小件快递 +* `future`:结果回收 + +--- + +# 二、`std::mutex`(最常用) + +## 1️⃣ `std::mutex` + +```cpp +#include + +std::mutex m; +``` + +### 作用 + +* 最普通的互斥锁 +* **同一时间只能一个线程进入临界区** + +--- + +## 2️⃣ `lock()` —— 上锁(阻塞) + +```cpp +m.lock(); +// 临界区 +m.unlock(); +``` + +### 特点 + +* 如果锁被占用 → **阻塞等待** +* 不可递归 + +### 返回值 + +❌ 无 + +### ❗新手警告 + +> **一旦忘记 `unlock()`,程序直接死锁** + +--- + +## 3️⃣ `unlock()` —— 解锁 + +```cpp +m.unlock(); +``` + +### 返回值 + +❌ 无 + +--- + +## 4️⃣ `try_lock()` —— 尝试上锁(不阻塞) + +```cpp +if (m.try_lock()) { + // 拿到锁 + m.unlock(); +} +``` + +### 返回值 + +* `true`:成功 +* `false`:失败 + +### 用途 + +* 避免卡死 +* 非关键任务 + +--- + +# 三、RAII 锁(**你真正该用的**) + +> **99% 的情况下,不要直接用 `lock()/unlock()`** + +--- + +## 5️⃣ `std::lock_guard`(最常用、最安全) + +```cpp +void foo(){ + std::lock_guard lock(m); + // 自动上锁 + // 自动解锁(作用域结束) +} +``` + +### 特点 + +* 构造时上锁 +* 析构时解锁 +* **不能手动 unlock** + +### 返回值 + +❌ 无 + +✔ 最安全 +✔ 最推荐 + +--- + +## 6️⃣ `std::unique_lock`(高级版) + +```cpp +std::unique_lock lock(m); +``` + +### 特点 + +* 可以: + + * 延迟上锁 + * 手动 unlock / lock + * 搭配 `condition_variable` + +### 常见用法 + +```cpp +std::unique_lock lock(m); +lock.unlock(); +// ... +lock.lock(); +``` + +### 返回值 + +❌ 无 + +📌 **灵活 ≠ 常用**,新手少用 + +--- + +## 7️⃣ 延迟上锁 + +```cpp +std::unique_lock lock(m, std::defer_lock); +// 之后再 lock() +lock.lock(); +``` + +--- + +## 8️⃣ 尝试上锁(不阻塞) + +```cpp +std::unique_lock lock(m, std::try_to_lock); +if (lock.owns_lock()) { + // 成功 +} +``` + +--- + +# 四、特殊 mutex 类型 + +## 9️⃣ `std::recursive_mutex` + +```cpp +std::recursive_mutex m; +``` + +### 特点 + +* 同一线程可以多次 lock +* 必须 unlock 相同次数 + +### 什么时候用? + +* 递归函数 +* 老代码 + +⚠️ **能不用就不用**,通常是设计有问题 + +--- + +## 🔟 `std::timed_mutex` + +```cpp +std::timed_mutex m; +``` + +### 作用 + +* 支持超时锁 + +```cpp +if (m.try_lock_for(100ms)) { + m.unlock(); +} +``` + +--- + +## 1️⃣1️⃣ `std::recursive_timed_mutex` + += `recursive_mutex` + 超时 + +--- + +# 五、一次锁多个 mutex(死锁克星) + +## 1️⃣2️⃣ `std::lock()` + +```cpp +std::lock(m1, m2); +// 全部锁成功 +``` + +### 特点 + +* 避免死锁 +* 要么全拿,要么全不拿 + +--- + +## 1️⃣3️⃣ `std::scoped_lock`(C++17) + +```cpp +std::scoped_lock lock(m1, m2); +``` + +✔ 自动 +✔ 安全 +✔ 推荐 + +--- + +# 六、完整 API 总表(收藏级) + +| API | 作用 | 返回值 | +| ----------------------- | ----------- | ---- | +| `mutex` | 基础互斥锁 | — | +| `lock()` | 阻塞上锁 | void | +| `unlock()` | 解锁 | void | +| `try_lock()` | 尝试上锁 | bool | +| `recursive_mutex` | 可递归锁 | — | +| `timed_mutex` | 超时锁 | — | +| `recursive_timed_mutex` | 递归+超时 | — | +| `lock_guard` | RAII 锁 | — | +| `unique_lock` | 高级 RAII 锁 | — | +| `scoped_lock` | 多锁 RAII | — | +| `lock()` | 同时锁多个 mutex | void | + +--- + +# 七、什么时候用 mutex?一句判断法 + +> **如果一个变量:** +> +> * 被多个线程访问 +> * 至少一个线程写 + +👉 **要么 mutex,要么 atomic** + +--- + +# 八、什么时候不要用 mutex? + +❌ 只是计数 +❌ 简单 flag +❌ 单一整数状态 + +👉 用 `atomic`,更快更简单。 + +--- + +# 九、新手不翻车口诀(记住这 4 句) + +1️⃣ **能不共享就不共享** +2️⃣ **能分任务就别加锁** +3️⃣ **能用 `lock_guard` 就别手写 lock** +4️⃣ **看到 `recursive_mutex` 先怀疑设计** + +--- + +你现在已经把 **thread + mutex** 两大核心吃下来了。 +下一步顺序我强烈建议: + +👉 **``(最后一块拼图)** +或者 +👉 **`condition_variable`(线程协作)** + +你选一个,我继续给你“端锅”。 diff --git a/Thread/Thread.md b/Thread/Thread.md new file mode 100644 index 0000000..e5c7ddc --- /dev/null +++ b/Thread/Thread.md @@ -0,0 +1,279 @@ +哈哈哈哈,**太正常了**,这玩意儿本来就反直觉 😄 +行,我给你一份**能当速查表用的 `` 全家桶说明**,不讲虚的,只讲: + +* 干嘛用 +* 怎么调 +* 有没有返回值 +* 什么时候该用 / 不该用 + +你看完这一份,再回头看你之前的代码,会突然“哦原来如此”。 + +--- + +# 一、`` 到底提供了什么? + +严格来说 `` **只解决三件事**: + +1. 创建线程 +2. 控制线程生命周期 +3. 获取线程身份 / 状态 + +👉 **不负责:** + +* 返回值 +* 同步 +* 通信 + +那些是 ``、``、`` 的活。 + +--- + +# 二、`std::thread`(核心中的核心) + +## 1️⃣ 构造线程(创建并立刻运行) + +```cpp +#include + +void foo(int x) {} + +std::thread t(foo, 10); +``` + +### 本质 + +* 创建 OS 线程 +* 立刻执行 `foo(10)` + +### 参数规则 + +* **按值拷贝** +* 要引用必须用 `std::ref` + +```cpp +int x = 10; +thread t(foo, ref(x)); +``` + +### 返回值 + +❌ **没有** +(线程函数的返回值会被丢弃) + +--- + +## 2️⃣ `join()` —— 等线程干完 + +```cpp +t.join(); +``` + +### 作用 + +* 阻塞当前线程 +* 直到 `t` 执行完 + +### 返回值 + +❌ 无 + +### 必须记住的铁律 + +> **一个 `std::thread`: +> 要么 `join()`,要么 `detach()`,否则程序直接 `std::terminate()`** + +--- + +## 3️⃣ `detach()` —— 放生线程(慎用) + +```cpp +t.detach(); +``` + +### 作用 + +* 线程变成后台线程 +* 主线程不再管它 + +### 返回值 + +❌ 无 + +### 什么时候能用? + +* 日志 +* 心跳 +* 不依赖任何外部对象 + +### 新手忠告 + +> **90% 的 `detach()` 都是 bug** + +--- + +## 4️⃣ `joinable()` —— 能不能 join? + +```cpp +if (t.joinable()) { + t.join(); +} +``` + +### 什么时候 false? + +* 已 join +* 已 detach +* 默认构造的 thread + +--- + +## 5️⃣ `get_id()` —— 线程 ID + +```cpp +std::thread::id id = t.get_id(); +``` + +### 用途 + +* 调试 +* 日志 +* 判断是不是同一个线程 + +--- + +## 6️⃣ `std::this_thread::get_id()` + +```cpp +auto id = std::this_thread::get_id(); +``` + +👉 获取**当前线程**的 ID + +--- + +# 三、线程“辅助函数”(非常常用) + +## 7️⃣ `sleep_for()` —— 休眠一段时间 + +```cpp +#include +using namespace std::chrono_literals; + +std::this_thread::sleep_for(100ms); +``` + +### 作用 + +* 让当前线程休眠 + +### 返回值 + +❌ 无 + +--- + +## 8️⃣ `sleep_until()` —— 睡到某个时间点 + +```cpp +auto t = chrono::steady_clock::now() + 1s; +std::this_thread::sleep_until(t); +``` + +--- + +## 9️⃣ `yield()` —— 让出 CPU + +```cpp +std::this_thread::yield(); +``` + +### 作用 + +* 告诉调度器: + “我不急,你先跑别人” + +### 用途 + +* 自旋锁 +* 低优先级任务 + +--- + +# 四、`hardware_concurrency()` —— CPU 有多少核? + +```cpp +unsigned n = std::thread::hardware_concurrency(); +``` + +### 返回值 + +* 建议线程数 +* **可能是 0(不保证)** + +👉 常见用法: + +```cpp +int n = max(1u, thread::hardware_concurrency()); +``` + +--- + +# 五、所有 `` API 一览表(重点) + +| API | 作用 | 返回值 | +| ---------------------------- | ------- | ------------ | +| `thread(f, args...)` | 创建并运行线程 | — | +| `join()` | 等线程结束 | void | +| `detach()` | 放生线程 | void | +| `joinable()` | 能否 join | bool | +| `get_id()` | 线程 ID | `thread::id` | +| `this_thread::get_id()` | 当前线程 ID | `thread::id` | +| `this_thread::sleep_for()` | 休眠 | void | +| `this_thread::sleep_until()` | 睡到时间点 | void | +| `this_thread::yield()` | 让出 CPU | void | +| `hardware_concurrency()` | CPU 线程数 | unsigned | + +📌 **没有一个 API 有“业务返回值”** + +--- + +# 六、那“返回值”到底去哪了? + +👉 **根本不在 ``** + +你要记住这个分工: + +| 需求 | 用什么 | +| ------ | ---------------- | +| 跑线程 | `std::thread` | +| 拿返回值 | `std::future` | +| 简单并发任务 | `std::async` | +| 同步 | `mutex / atomic` | + +--- + +# 七、你现在可以这样理解(非常重要) + +> **`std::thread` ≈ C 语言 `pthread_create` +> `std::async` ≈ “有返回值的线程函数”** + +所以你刚才的崩溃是合理的 😂 +你是拿 **pthread 的心态**,期待 **Java 的体验**。 + +--- + +# 八、给你一条“永远不翻车”的使用准则 + +> **如果你想要返回值: +> ❌ 不要用 `std::thread` +> ✅ 用 `std::async`** + +--- + +如果你愿意,下一步我可以帮你做三件事之一: + +1️⃣ 把你刚才那个项目 **改成 async 版本** +2️⃣ 给你一份 **`` 函数大全(和这份一样清楚)** +3️⃣ 手把手写一个 **线程池(真正工程级)** + +你挑。