diff --git "a/multiThread_notes/c++345円244円232円347円272円277円347円250円213円_12_30.pptx" "b/multiThread_notes/c++345円244円232円347円272円277円347円250円213円_12_30.pptx" new file mode 100644 index 0000000..2c23406 Binary files /dev/null and "b/multiThread_notes/c++345円244円232円347円272円277円347円250円213円_12_30.pptx" differ diff --git "a/multiThread_notes/351円231円204円344円273円266円/C++11345円244円232円347円272円277円347円250円213円347円233円270円345円205円263円.md" "b/multiThread_notes/351円231円204円344円273円266円/C++11345円244円232円347円272円277円347円250円213円347円233円270円345円205円263円.md" new file mode 100644 index 0000000..208d6dc --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/C++11345円244円232円347円272円277円347円250円213円347円233円270円345円205円263円.md" @@ -0,0 +1,299 @@ +[TOC] + + + +# C++11 + +## RAII线程管理 + +* 每个[std::thread](https://en.cppreference.com/w/cpp/thread/thread)对象都处于可合并(joinable)或不可合并(unjoinable)的状态。 + +* 一个**可合并**的[std::thread](https://en.cppreference.com/w/cpp/thread/thread)对应于一个底层**异步运行的线程**,若底层线程处于**阻塞、等待调度或已运行结束**的状态,则此[std::thread](https://en.cppreference.com/w/cpp/thread/thread)可合并,否则不可合并。 + +* 不可合并的[std::thread](https://en.cppreference.com/w/cpp/thread/thread)包括: + + * 默认构造的[std::thread](https://en.cppreference.com/w/cpp/thread/thread):此时**没有要运行的函数**,因此没有对应的底层运行线程 + * 已移动的[std::thread](https://en.cppreference.com/w/cpp/thread/thread):**移动操作**导致底层线程被转用于另一个[std::thread](https://en.cppreference.com/w/cpp/thread/thread) + * 已[join](https://en.cppreference.com/w/cpp/thread/thread/join)或已[join](https://en.cppreference.com/w/cpp/thread/thread/join)的[std::thread](https://en.cppreference.com/w/cpp/thread/thread) + +* 如果**可合并的[std::thread](https://en.cppreference.com/w/cpp/thread/thread)对象的析构函数被调用,则程序的执行将终止** + +* **析构可合并的[std::thread](https://en.cppreference.com/w/cpp/thread/thread)时,隐式[join](https://en.cppreference.com/w/cpp/thread/thread/join)或隐式[detach](https://en.cppreference.com/w/cpp/thread/thread/detach)的带来问题更大**。隐式[join](https://en.cppreference.com/w/cpp/thread/thread/join)导致 g 运行结束时仍要保持等待 f 运行结束,这就会导致性能问题,且调试时难以追踪原因。隐式[detach](https://en.cppreference.com/w/cpp/thread/thread/detach)导致的调试问题更为致命 + +* ```c++ + void f(int&) {} //引用 + void g(){ + int i = 1; + std::thread t(f, i); + } // 如果隐式detach,局部变量i被销毁,但f仍在使用局部变量的引用 + ``` +## 用[std::async](https://en.cppreference.com/w/cpp/thread/async)替代[std::thread](https://en.cppreference.com/w/cpp/thread/thread) + +- **如果函数有返回值**,[std::thread](https://en.cppreference.com/w/cpp/thread/thread)无法直接获取该值,而[std::async](https://en.cppreference.com/w/cpp/thread/async)返回[std::future](https://en.cppreference.com/w/cpp/thread/future)提供了[get](https://en.cppreference.com/w/cpp/thread/future/get)来获取该值。 + +- 如果函数抛出异常,**[get](https://en.cppreference.com/w/cpp/thread/future/get)能访问异常,而[std::thread](https://en.cppreference.com/w/cpp/thread/thread)会调用[std::terminate](https://en.cppreference.com/w/cpp/error/terminate)终止程序** + +- ```c++ + int f() noexcept; + std::thread t(f); // 若无线程可用,仍会抛出异常 + ``` + +### [std::async](https://en.cppreference.com/w/cpp/thread/async)异步求值 + +**需求:**获取运行在后台的计算结果,而[std::thread](https://en.cppreference.com/w/cpp/thread/thread)不能获取返回值 + +先用[std::async](https://en.cppreference.com/w/cpp/thread/async)启动一个异步任务,它返回一个持有计算结果的[std::future](https://en.cppreference.com/w/cpp/thread/future),通过[std::future::get](https://en.cppreference.com/w/cpp/thread/future/get)即可阻塞线程,直到期值的状态为ready并返回该结果 + +```c++ +int f(int x){ + if (x < 0){ + throw std::out_of_range("x < 0"); + } + return x; +} + +int main(){ + //需求 + std::thread t(f,1); // 如何读取f的返回值? + t.join(); + + // + std::future ft = std::async(f,1);//异步任务 + std::cout << ft.get(); //可阻塞线程,并获取值 1 + + std::future ft2 = std::async(f,-1);//异步任务 + int x = ft.get(); // 抛出已存储的异常 + std::cout << x; +} +``` + +### [std::async](https://en.cppreference.com/w/cpp/thread/async)和[std::thread](https://en.cppreference.com/w/cpp/thread/thread)支持额外的函数传参 + +```c++ +// 函数 +int f(int); +auto ft = std::async(f, 42); + +// 成员函数 +struct A { + int f(int); +}; + +A a; +auto ft1 = std::async(&A::f, &a, 42); // 调用p->f(42),p是指向x的指针 +auto ft2 = std::async(&A::f, a, 42); // 调用tmpa.f(42),tmpa是a的副本 + +// 函数对象 +struct A { + int operator()(int); +}; +A a; +auto ft1 = std::async(A(), 42); // 调用tmpa(42),tmpa由A的移动构造函数获得 +auto ft2 = std::async(std::ref(a), 42); // 调用a(42) +``` + +### [std::async](https://en.cppreference.com/w/cpp/thread/async)启动策略: + +```c++ + int f(); + auto ft1 = std::async(std::launch::async, f);// 函数必须异步执行,即运行在不同的线程上 + auto ft2 = std::async(std::launch::deferred, f);// 函数只在返回的期值调用get或wait时运行 + // 不指定取 或运算的结果 + // auto ft3 = std::async(f)等价于 + auto ft3 = std::async(std::launch::async | std::launch::deferred, f); +``` + +## [std::packaged_task](https://en.cppreference.com/w/cpp/thread/packaged_task) + +除了[std::async](https://en.cppreference.com/w/cpp/thread/async),还可以用[std::packaged_task](https://en.cppreference.com/w/cpp/thread/packaged_task)让[std::future](https://en.cppreference.com/w/cpp/thread/future)与任务关联 + +如果[std::packaged_task](https://en.cppreference.com/w/cpp/thread/packaged_task)直到析构都未设置值,[std::future::get](https://en.cppreference.com/w/cpp/thread/future/get)也会抛出[std::future_error](https://en.cppreference.com/w/cpp/thread/future_error)异常 + +```c++ +int f(); +std::packaged_task pt(f); +auto ft = pt.get_future(); +pt(); // 调用std::packaged_task对象,将std::future设为就绪 +std::cout << ft.get(); +``` + +## [std::promise](https://en.cppreference.com/w/cpp/thread/promise) + +* [std::promise](https://en.cppreference.com/w/cpp/thread/promise)可以显式设置值 +* 如果[std::promise](https://en.cppreference.com/w/cpp/thread/promise)直到析构都未设置值,[std::future::get](https://en.cppreference.com/w/cpp/thread/future/get)也会抛出[std::future_error](https://en.cppreference.com/w/cpp/thread/future_error)异常 +* 一个[std::promise](https://en.cppreference.com/w/cpp/thread/promise)只能关联一个[std::future](https://en.cppreference.com/w/cpp/thread/future),关联多次时将抛出[std::future_error](https://en.cppreference.com/w/cpp/thread/future_error)异常 + +```c++ +std::promise ps; +std::future ft = ps.get_future(); +ps.set_value(42); // set_value还会将状态设置为就绪 +std::cout << ft.get(); // 42 +``` + +#### 在线程间对状态发送信号 + +```c++ +void f(std::promise ps){ + std::this_thread::sleep_for(std::chrono::seconds(1)); + ps.set_value(); +} + +int main(){ + std::promise ps; + std::future ft = ps.get_future(); + std::thread t(f, std::move(ps)); + ft.wait(); // 阻塞直到set_value,相当于没有返回值的get + t.join(); +} +``` + +```c++ +int f(); +int main(){ + std::future ft; + { + std::packaged_task pt(f); + ft = pt.get_future(); + // std::promise ps; + // ft = ps.get_future(); + } + ft.get(); // 抛出异常 +} +``` + +### [std::shared_future](https://en.cppreference.com/w/cpp/thread/shared_future) + +* [std::future](https://en.cppreference.com/w/cpp/thread/future)调用[get](https://en.cppreference.com/w/cpp/thread/future/get)后就无法再次[get](https://en.cppreference.com/w/cpp/thread/future/get),也就是说只能获取一次数据,此外还会导致所在线程与其他线程数据不同步。[std::shared_future](https://en.cppreference.com/w/cpp/thread/shared_future)就可以解决此问题 + +```cpp +std::promise ps; +std::future ft(ps.get_future()); +assert(ft.valid()); +std::shared_future sf(std::move(ft)); +assert(!ft.valid()); +assert(sf.valid()); +``` + +* 也可以直接构造 + +```cpp +std::promise ps; +// std::future隐式转换为std::shared_future +std::shared_future sf(ps.get_future()); +``` + +* 用[std::future::share](https://en.cppreference.com/w/cpp/thread/future/share)可以直接生成[std::shared_future](https://en.cppreference.com/w/cpp/thread/shared_future),这样就可以直接用auto简化声明[std::shared_future](https://en.cppreference.com/w/cpp/thread/shared_future) + +```cpp +std::promise ps; +auto sf = ps.get_future().share(); +``` + +* 每一个[std::shared_future](https://en.cppreference.com/w/cpp/thread/shared_future)对象上返回的结果不同步,为了避免多线程访问同一[std::shared_future](https://en.cppreference.com/w/cpp/thread/shared_future)对象时的数据竞争就必须加锁保护。更好的方法是给每个线程拷贝一个[std::shared_future](https://en.cppreference.com/w/cpp/thread/shared_future)对象,这样就可以安全访问而无需加锁 + +## std::chrono 时间库 + +计时库包含一组实用程序函数和类型,用于处理持续时间、时钟和时间点。 + +* 当前时间:[std::chrono::system_clock::now()](https://en.cppreference.com/w/cpp/chrono/system_clock/now) +* 时间点: [std::chrono::time_point](https://en.cppreference.com/w/cpp/chrono/time_point) + +### 打印当前系统时间 + +```c++ +#include // std::cout +#include // std::put_time +#include // std::time_t, struct std::tm, std::localtime +#include // std::chrono::system_clock +int main(int argc, char *argv[]) +{ + using namespace std::chrono; + auto now = std::chrono::system_clock::now(); + std::time_t now_c = std::chrono::system_clock::to_time_t(now); // 转为整数 + std::cout << std::put_time(std::localtime(&now_c), "%c"); + return 0; +} +``` + +### [std::chrono::time_point](https://en.cppreference.com/w/cpp/chrono/time_point) + +* time_point是表示时间的类型,值为从某个时间点(比如unix时间戳:1970年1月1日0时0分)开始计时的时间长度 + +```c++ +auto x = std::chrono::high_resolution_clock::now(); +// auto x ==> +//std::chrono::system_clock::time_point +``` + +### [std::chrono::duration](https://en.cppreference.com/w/cpp/chrono/duration) + +* 标准库提供了表示时间间隔类型的[std::chrono::duration]( + +```c++ +using namespace std::chrono; +auto startTimer = std::chrono::high_resolution_clock::now(); +std::this_thread::sleep_for(std::chrono::seconds(1)); +auto EndTimer = std::chrono::high_resolution_clock::now(); +std::chrono::duration fp_ms = EndTimer - startTimer; +std::cout << "duration:"<< fp_ms.count() << " ms"< g = f; //存储函数 + +struct X { + void operator()(int n) const{ + std::cout << n; + } +}; + +std::function f = X(); //存储函数对象 +std::function f = [] (int i) { std::cout << i; }; //存储 lambda +std::function g = std::bind(f, std::placeholders::_1); //存储 bind 对象 + +struct A { + int i = 1; + void f(int n) { std::cout << n; } +}; +std::function g = &A::f; //存储成员函数 +std::function g = &A::i; //存储数据成员 + +``` + +### [std::atomic](https://en.cppreference.com/w/cpp/atomic/atomic)提供原子操作,volatile禁止优化内存 + +编译器或底层硬件对于**不相关的赋值会重新排序**以提高代码运行速度,[std::atomic](https://en.cppreference.com/w/cpp/atomic/atomic)可以限制重排序以**保证顺序一致性** \ No newline at end of file diff --git "a/multiThread_notes/351円231円204円344円273円266円/myThread.md" "b/multiThread_notes/351円231円204円344円273円266円/myThread.md" new file mode 100644 index 0000000..32620ec --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/myThread.md" @@ -0,0 +1,732 @@ + + +[TOC] + + + +# 0 进程与线程 + +## 0.0 术语 + +> 参考: +> +> [1] [note/操作系统.md at master · arkingc/note · GitHub](https://github.com/arkingc/note/blob/master/操作系统/操作系统.md#1进程的定义) +> +> [2] [多线程的同步与互斥(互斥锁、条件变量、读写锁、自旋锁、信号量)_青萍之末的博客-CSDN博客_线程互斥锁](https://blog.csdn.net/daaikuaichuan/article/details/82950711) + +- **进程/process** : 是操作系统进行**资源分配**的基本单位 + +> 进程是"内存中正在运行的程序"。`Linux` 操作系统通过`fork()` 系统调用产生. + +- **线程** :是一个基本的CPU执行单元 ,是**调度**的基本单位 + +> 通过**共享地址空间**,从而可以高效地共享数据。 + + 背景: + +> 在多任务操作系统中,同时运行的**多个任务可能**: +> +> - 都需要**访问/使用同一种资源**; +> - 多个任务之间有依赖关系,某个任务的运行**依赖**于另一个任务。 + +- **临界资源**: 是一次仅允许一个进程使用的共享资源. + +- **临界区**:一段**代码**,执行代码的进程将访问**临界资源**. + +- **原子性操作**:指令序列对外不可分割结果只有,成功和什么也不做。 + +- **死锁**:一组互相竞争资源的线程因**循环等待**,导致"永久"**阻塞**的现象 + +死锁条件: + +1. **互斥**:一次只有一个进程可以使用一个资源 +2. **占有且等待**:当一个进程等待其他进程时,继续占有已经分配的资源 +3. **不可抢占**:不能强行抢占进程已占有的资源 +4. **循环等待**:存在一个封闭的进程链,使得每个进程至少占有此链中下一个进程所需的一个资源 + +[![img](myThread.assets/os-4-1.png)](https://github.com/arkingc/note/blob/master/pic/os-4-1.png) + +## 0.1 通信(同步)方式 + +> 通信方面:**进程间**通信需要借助操作系统,**线程间**直接读/写进程数据段。 + +**进程**: 推荐使用 + +- **套接字( socket )** + +> TCP 是双向的 +> +> 进程独占 +> +> 操作系统会自动回收 +> +> TCP 是字节流协议,只能顺序读取,有写缓冲 + +**线程** :推荐使用 **互斥器(mutex)**与 **条件变量(condition variable)** + +- **互斥锁(mutex)** +- 互斥器(mutex) 恐怕是使用得最多最简单的的同步原语. + + + - 保护了临界区,一个时刻最多只能有一个线程在临界区内活动 + +- **条件变量(condition variable)** + + - 是一个或多个线程等待某个布尔表达式为真, + 即等待别的线程"唤醒"它。 + + - 条件变量是非常底层的同步原语,很少直接使用. + +# 1 线程管理的基础 + +> - 启动新线程 +> - 等待线程与分离线程 +> - 线程唯一标识符 + +## 1.0 std::thread前置知识 + +- 线程都有入口函数,入口`main`/`foo` 函数,两者会同时运行. +- 线程会在函数结束时完会退出. +- 使用[join](https://en.cppreference.com/w/cpp/thread/thread/join)即可保证局部变量在线程结束后才被销毁. +- [join](https://en.cppreference.com/w/cpp/thread/thread/join)过的[std::thread](https://en.cppreference.com/w/cpp/thread/thread) joinable()为false,故不能再次被[join](https://en.cppreference.com/w/cpp/thread/thread/join) +- 使用[detach](https://en.cppreference.com/w/cpp/thread/thread/detach)分离线程会让线程在后台运行,线程分离后与主线程无法直接交互,也不能被[join](https://en.cppreference.com/w/cpp/thread/thread/join) +- 分离线程称为守护线程,即没有任何显式接口并运行在后台的线程,其特点是长时间运行。 +- 线程运行过程中发生异常,之后调用的[join](https://en.cppreference.com/w/cpp/thread/thread/join)会被忽略,为此需要捕获异常并在处理异常时调用[join](https://en.cppreference.com/w/cpp/thread/thread/join) +- 使用RAII类来管理[std::thread](https://en.cppreference.com/w/cpp/thread/thread) + +## 1.1 启动线程std::thread + +- 将函数添加为[std::thread](https://en.cppreference.com/w/cpp/thread/thread)的参数即可启动线程 +- 线程启动后,销毁前要调用[join](https://en.cppreference.com/w/cpp/thread/thread/join)或[detach](https://en.cppreference.com/w/cpp/thread/thread/detach),否则[std::thread](https://en.cppreference.com/w/cpp/thread/thread)的析构函数会调用[std::terminate](https://en.cppreference.com/w/cpp/error/terminate)终止程序. + +```C++ +#include +#include +#include +void hello(){ + std::cout << "Hello thread:"<< std::this_thread::get_id()< +#include +#include +void func_byPass(int value = 1){ std::cout << __func__ <<":"<< value < p){ + std::cout << __func__ <<":"<< *p< p(new int(43)); + std::thread t4(FuncPoint, std::move(p)); + t4.join(); + std::this_thread::sleep_for(std::chrono::seconds(1)); +} + +``` + +## 1.3 转移线程所有权 + +```c++ +void some_function(); +void some_other_function(); +std::thread t1(some_function); // 1,some_function与t1关联 +std::thread t2=std::move(t1); // 2,some_function与t2关联 +t1=std::thread(some_other_function); // 3,临时thread,隐形调用move +std::thread t3; // 4,默认构造函数,无执行线程关联 +t3=std::move(t2); // 5 +// t1 ==> some_function +//t3 = t2 ==> some_function +//直接调用std::terminate()终止程序继续运行 +t1=std::move(t3); // 6 赋值操作将使程序崩溃 +// std::terminate()是noexcept函数)是为了保证与std::thread的析构函数的行为一致。 +//需要在线程对象被析构前,显式的等待线程完成,或者分离它;进行赋值时也需要满足这些条件 +``` + +## 1.4 线程标识 + +```c++ +std::thread::id master_thread; +void some_core_part_of_algorithm() +{ + if(std::this_thread::get_id()==master_thread) + { + do_master_thread_work(); + } + do_common_work(); +} +std::cout< std::mem_fn的作用就是将**类的成员函数**转换为一个**可调用对象** . +> +> include + +```c++ +void do_work(unsigned id); +void f() +{ + std::vector threads; + for(unsigned i=0; i < 20; ++i){ + threads.push_back(std::thread(do_work,i)); // 产生线程 + } + std::for_each(threads.begin(),threads.end(), + std::mem_fn(&std::thread::join)); // 对每个线程调用join() +} +``` + +# 2 线程间共享数据 + +> - 共享数据带来的问题 +> - 使用互斥量`mutex`保护数据 +> - 数据保护的替代方案 + +## 2.0 线程间共享数据存在的问题 + +> 条件竞争 `race condition` : 一个任务的输出依赖于不受控制的事件出现顺序. +> +> 数据竞争`data race` :并发的去修改同一数据,导致数据被破坏 数据竞争是未定义行为。 +> +> 出现条件竞争`race condition`的问题时很难复现,因此编程时需要使用大量复杂操作来避免. + +### 2.0.0 经典的race condition + + **局部静态变量是数据竞争的一种常见来源**。 + +```c++ +int get_id() { + static int id = 1; + return id++; +} +``` + +```C++ +//先检查非空再获取栈顶元素,在单线程中是安全的 +std::stack s; +if (!s.empty()) +{ + int n = s.top(); //1 + s.pop(); //2 +} +//case1: AB检查非空后,线程A先pop,会导致线程B的top出错. +//case2: AB检查非空后,线程A和线程B同时执行完1,同一个值被处理了2次,看起来没问题,且不好定位bug. +``` + +**思考: **为什么不直接让pop返回栈顶元素? + +假设有一个`stack>`,拷贝vector时需要在堆上分配内存,如果系统负载严重或资源有限,vector的**拷贝构造函数**就会抛出[std::bad_alloc](https://en.cppreference.com/w/cpp/memory/new/bad_alloc)异常, + +**返回值**这个动作是**最后执行**,stack在返回前已经弹出了元素,但如果拷贝返回值时抛出异常,就会导致弹出的数据丢失(从栈上移除但拷贝失败)。 + +```c++ +//伪代码 +stack移除 A +拷贝构造A ==> 抛出std::bad_alloc异常,打断流程. +拷贝返回值A +返回结果 +``` + +下面思考几种把top和pop合为一步的方法。 + +**case A 传入参数获取结果值** + +```C++ +std::vector res; +s.pop(res); +//需要构造一个栈元素类型的实例 +//1. 资源消耗大 +//2. 可能元素不支持 =赋值操作 +//3. 可能构造对象的时候要参数, +``` + +**caseB 将元素类型classA设置不抛异常的拷贝或移动构造函数** + +```c++ +stack> s; +``` + +**case C** 返回指向弹出元素的指针,指针可以自由拷贝且不会抛异常。这需要管理对象的内存分配,使用[std::shared_ptr](https://en.cppreference.com/w/cpp/memory/shared_ptr)是个不错的选择.**但这个方案的开销太大,尤其是对于内置类型**. + +**case D** 结合方案AB或者AC实现一个线程安全的stack + +* 之前锁的粒度(锁保护的数据量大小)太小,保护操作覆盖不周全,这里的粒度就较大,覆盖了大量操作。但并非粒度越大越好,如果锁粒度太大,过多线程请求竞争占用资源时,并发的性能提升就被抵消掉了 +* 如果**给定操作需要多个mutex时,就会引入一个新的潜在问题,即死锁** + +```c++ +#include +#include +#include +#include + +struct emptyStack : std::exception +{ + const char* what() const noexcept + { + return "empty stack!"; + } +}; + +template +class A { + private: + std::stack s; + mutable std::mutex m; + public: + A() : s(std::stack()) {} + + A(const A& rhs) + { + std::lock_guard l(rhs.m); + s = rhs.s; + } + + A& operator=(const A&) = delete; + + void push(T n) + { + std::lock_guard l(m); + s.push(std::move(n)); + } + + std::shared_ptr pop() // 返回一个指向栈顶元素的指针 + { + std::lock_guard l(m); + if (s.empty()) throw emptyStack(); + const std::shared_ptr res(std::make_shared(std::move(s.top()))); + s.pop(); + return res; + } + + void pop(T& n) // 传引用获取结果 + { + std::lock_guard l(m); + if (s.empty()) throw emptyStack(); + n = std::move(s.top()); + s.pop(); + } + + bool empty() const + { + std::lock_guard l(m); + return s.empty(); + } +}; +``` + + + +## 2.1 使用mutex保护共享数据 + +* 使用mutex在访问共享数据前加锁,访问结束后解锁。一个线程用特定的mutex锁定后,其他线程必须等待该线程的mutex解锁才能访问共享数据. +* C++可用[std::mutex](https://en.cppreference.com/w/cpp/thread/mutex)来创建mutex,可通过[std::mutex::lock](https://en.cppreference.com/w/cpp/thread/mutex/lock)加锁,通过[std::unlock](https://en.cppreference.com/w/cpp/thread/mutex/unlock)解锁,但一般不直接使用. +* [std::lock_guard](https://en.cppreference.com/w/cpp/thread/lock_guard)是一个用[std::mutex](https://en.cppreference.com/w/cpp/thread/mutex)构造的RAII模板类(C++11) + +### 2.1.0 一些问题 + + **避免未释放的锁定** + +```c++ +mutex mtx; +​ +void do_stuff() +{ + mtx.lock(); + // ... 做一些事 ... + mtx.unlock(); +} +``` + + **下面将导致死锁** + +``` +// 线程 1 +lock_guard lck1(m1); +lock_guard lck2(m2); +​ +// 线程 2 +lock_guard lck2(m2); +lock_guard lck1(m1); +``` + +解决 + +> ### 用 `std::lock()` 或 `std::scoped_lock` 来获得多个 `mutex` +> +> ```c++ +> 代之以使用 lock(): +> +> // 线程 1 +> lock(m1, m2); +> lock_guard lck1(m1, defer_lock); +> lock_guard lck2(m2, defer_lock); +> ​ +> // 线程 2 +> lock(m2, m1); +> lock_guard lck2(m2, defer_lock); +> lock_guard lck1(m1, defer_lock); +> //或者(这样更佳,但仅为 C++17): +> +> // 线程 1 +> scoped_lock lck1(m1, m2); +> ​ +> // 线程 2 +> scoped_lock lck2(m2, m1); +> //这样,thread1 和 thread2 的作者们仍然未在 mutex 的顺序上达成一致,但顺序不再是问题了。 +> ``` + +* ```c++ + #include + #include + #include + + std::list v; + std::mutex m; + + void f(int n) + { + std::lock_guard l(m); + //std::lock_guard l(m); //c++17 引入了类模板实参推断,简写 + v.emplace_back(n); + } + + bool listContains(int n) + { + std::lock_guard l(m); + return std::find(std::begin(v), std::end(v), n) != std::end(v); + } + ``` + +* C++17 提供加强版的[std::scoped_lock](https://en.cppreference.com/w/cpp/thread/scoped_lock),它可以接受任意数量的[std::mutex](https://en.cppreference.com/w/cpp/thread/mutex),可完全取代[std::lock_guard](https://en.cppreference.com/w/cpp/thread/lock_guard) + + ```c++ + #include + std::mutex m1,m2; + std::scoped_lock g(m1, m2); + ``` + +> **注意** +> +> 一般mutex和要保护的数据一起放在类中,定义为private数据成员。 +> +> 若某个成员函数返回指向**数据成员的指针或引用**,则通过这个**指针的访问行为不会被mutex限制**,因此需要谨慎设置接口,确保mutex能锁住数据 + +* 锁的粒度太小,保护操作覆盖不周全。 + +* 如果锁粒度太大,过多线程请求竞争占用资源时,并发的性能提升就被抵消掉了. + +* 如果给定操作**需要多个mutex时**,引入**潜在问题----------死锁**. + +### 2.1.0 死锁 + +> * 死锁的四个必要条件:**互斥、占有且等待、不可抢占、循环等待** +> +> * 避免死锁**多个mutex以相同顺序上锁**,总是先锁A再锁B,但这并不适用所有情况 +> +> * [std::lock](https://en.cppreference.com/w/cpp/thread/lock)解决了此问题,它可以**一次性锁住多个mutex,并且没有死锁风险** +> +> * [std::lock](https://en.cppreference.com/w/cpp/thread/lock)可能抛异常,此时就不会上锁,因此具有原子性**要么都锁住,要么都不锁** +> +> * 如果支持C++17,最易最优的同时上锁方法是使用[std::scoped_lock](https://en.cppreference.com/w/cpp/thread/scoped_lock) +> +> * ```c++ +> // 线程 1 +> scoped_lock lck1(m1, m2); +> // 线程 2 +> scoped_lock lck2(m2, m1); +> ``` +> ``` +> +> ``` + +### 2.1.1 解决死锁 + +* 第一个建议是,一个线程**已经获取一个锁时就不要获取第二个**。如果每个线程只有一个锁,锁上就不会产生死锁(但除了互斥锁,其他方面也可能造成死锁,比如即使无锁,线程间相互等待也可能造成死锁) + +* 第二个建议是,**持有锁时避免调用用户提供的代码**。用户提供的代码可能做任何时,包括获取锁,如果持有锁时调用用户代码获取锁,就会违反第一个建议,并造成死锁。但有时调用用户代码是无法避免的 + +* 第三个建议是,**按固定顺序获取锁**。如果必须获取多个锁且不能用[std::lock](https://en.cppreference.com/w/cpp/thread/lock)同时获取,最好在每个线程上用固定顺序获取。上面的例子虽然是按固定顺序获取锁,但如果不同时加锁就会出现死锁,对于这种情况的建议是额外规定固定的调用顺序 + +* 第四个建议是**使用层次锁,如果一个锁被低层持有,就不允许再上锁** + + ```c++ + // 设定值来表示层级 + hierarchical_mutex high(10000); + hierarchical_mutex mid(6000); + hierarchical_mutex low(5000); + + void lf() // 最低层函数 + { + std::scoped_lock l(low); + } + + void hf() + { + std::scoped_lock l(high); + lf(); // 可以调用低层函数 + } + + void mf() + { + std::scoped_lock l(mid); + hf(); // 中层调用了高层函数,违反了层次结构 + } + ``` + + - **实现hierarchical_mutex** + + ```c++ + class A { + std::mutex m; // 内部锁 + int val; // 当前层级值 + int pre; // 用于保存前一线程层级值 + static thread_local int tVal; // tVal存活于一个线程周期 + public: + explicit A(int x) : val(x), pre(0) {} + void lock() + { + if (tVal> val) + { // 存储线程层级值tVal到pre后将其更新为锁的层级值val + m.lock(); + pre = tVal; + tVal = val; + } + else + { + throw std::logic_error("mutex hierarchy violated"); + } + } + void unlock() + { // 恢复线程层级值为pre + if (tVal != val) + { + throw std::logic_error("mutex hierarchy violated"); + } + tVal = pre; + m.unlock(); + } + bool try_lock() + { + if (tVal> val) + { + if (!m.try_lock()) return false; + pre = tVal; + tVal = val; + return true; + } + else + { + throw std::logic_error("mutex hierarchy violated"); + } + } + }; + + thread_local int A::tVal(INT_MAX); // 保证初始构造std::scoped_lock正常 + ``` + + +## 2.2 其他保护共享数据的可选方式 + +### 2.2.0 保护共享数据的初始化 + +- 一个极端但常见的情况是,共享数据在并发访问和初始化都需要保护,但之后要隐式同步。数据初始化后上锁只是为了保护初始化过程,但这会不必要地影响性能 +- 延迟初始化在单线程中很常见 + +```cpp +std::shared_ptr P; +void f(){ + if (!p){ + p.reset(new A); // 在多线程中这里需要保护 + } + p->doSomething(); +} +``` + +* 但在多线程直接上锁会导致不必要的线程资源阻塞 + +```c++ +std::shared_ptr p; +std::mutex m; + +void f(){ + std::unique_lock l(m); // 所有线程会在此处阻塞 + if (!p){ + p.reset(new A); + } + l.unlock(); + p->doSomething(); +} +``` + +* 很多人能想到一个更好的方法是双重检查锁模式 + +```cpp +void f(){ + if (!p) {// 这里没被锁保护,会与其他线程中被锁保护的reset竞争 + std::scoped_lock l(m); + if (!p){ + p.reset(new A); + } + } + p->doSomething(); +} +//第一次的检查没上锁,可能与其他线程中被保护的reset操作产生竞争。 +p.reset(new A); +// 1. 为A对象分配一片内存 +// 2. 在分配的内存上调用A的构造函数,构造一个A对象 +// 3. 返回该内存的指针,让p指向该内存 +// 编译器不一定按23顺序执行,可能32 +``` + +- 为了处理race condition,C++标准库提供了[std::once_flag](https://en.cppreference.com/w/cpp/thread/once_flag)和[std::call_once](https://en.cppreference.com/w/cpp/thread/call_once) + +```c++ +std::shared_ptr p; +std::once_flag flag; + +void init() +{ + p.reset(new A); +} + +void f() +{ + std::call_once(flag, init); //比使用mutex的开销更小 + p->doSomething(); +} +``` + +- static变量的初始化存在潜在的race condition:变量声明为static时,声明后就完成了初始化,一个线程完成了初始化,其他线程仍会抢着定义这个变量。 + +```c++ +class A { +public: + static A& getInstance(); + A(const A&) = delete; + A& operator(const A&) = delete; +private: + A() = default; + ~A() = default; +}; + +A& A::getInstance() +{//C++11规定static变量的初始化只完全发生在一个线程中. + static A instance; // C++11 ,线程安全的初始化 + return instance; +} +``` + +### 2.2.1 保护不常更新的数据结构 + +* 有些**数据需要经常访问但更新频率很低**,如果用[std::mutex](https://en.cppreference.com/w/cpp/thread/mutex)保护数据有些过度(大量读的操作也会因锁而影响性能),这就需要用上读写锁(reader-writer mutex),它允许多个线程并发读但仅一个线程写 +* C++17提供了[std::shared_mutex](https://en.cppreference.com/w/cpp/thread/shared_mutex)和[std::shared_timed_mutex](https://en.cppreference.com/w/cpp/thread/shared_timed_mutex)(C++14),后者比前者提供了更多操作,但前者性能更高。C++11没有提供读写锁,为此可使用[boost::shared_mutex](https://www.boost.org/doc/libs/1_71_0/doc/html/thread/synchronization.html#thread.synchronization.mutex_types.shared_mutex) +* 读写锁并不是万能的,其性能与处理器数量及读写线程的负载有关 +* C++14提供了[std::shared_lock](https://en.cppreference.com/w/cpp/thread/shared_lock),用法和[std::unique_lock](https://en.cppreference.com/w/cpp/thread/unique_lock)相同,此外[std::shared_lock](https://en.cppreference.com/w/cpp/thread/shared_lock)还允许多线程同时获取共享锁,因此一般用[std::shared_lock](https://en.cppreference.com/w/cpp/thread/shared_lock)锁定读,[std::unique_lock](https://en.cppreference.com/w/cpp/thread/unique_lock)锁定写 + +```cpp +class A { +private: + mutable std::shared_mutex m; //C++17 + int n = 0; +public: + int read(){ + std::shared_lock l(m); //C++14,锁定读 + return n; + } + void write(){ + std::unique_lock l(m); //C++14,锁定写 + ++n; + } +}; +``` + +### 2.2.2 递归锁 + +* 一个线程已经获取[std::mutex](https://en.cppreference.com/w/cpp/thread/mutex)(即已上锁)后再次上锁就会产生未定义行为 + +```c++ +std::mutex m; + +void f(){ + m.lock(); + m.unlock(); +} + +void g(){ + m.lock(); + f(); + m.unlock(); +} + +int main(){ + std::thread t(g); + t.join(); // 产生未定义行为 +} +``` + +* 为了允许这种情况,C++提供了[std::recursive_mutex](https://en.cppreference.com/w/cpp/thread/recursive_mutex),它可以在一个线程上多次获取锁,但在其他线程获取锁之前必须释放所有的锁 +* 多数情况下,如果需要递归锁,说明代码设计存在问题。比如一个类的每个成员函数都会上锁,一个成员函数调用另一个成员函数,就可能多次上锁,这种情况用递归锁就可以避免产生未定义行为。但显然这个设计本身是有问题的,更好的办法提取其中一个函数作为**private成员并且不上锁**,其他成员先上锁再调用该函数 + +# 3. 同步的并发操作 + +> - 带有future的等待 +> - 在限定时间内等待 +> - 使用同步操作简化代码 + + + + + + + + + + + + + + + + + +refs: + + + +https://stackoverflow.com/questions/40550730/how-to-implement-timeout-for-function-in-c + diff --git "a/multiThread_notes/351円231円204円344円273円266円/pdf/multithreaded_server.pdf" "b/multiThread_notes/351円231円204円344円273円266円/pdf/multithreaded_server.pdf" new file mode 100644 index 0000000..cec75fe Binary files /dev/null and "b/multiThread_notes/351円231円204円344円273円266円/pdf/multithreaded_server.pdf" differ diff --git "a/multiThread_notes/351円231円204円344円273円266円/ppt.txt" "b/multiThread_notes/351円231円204円344円273円266円/ppt.txt" new file mode 100644 index 0000000..820128b --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/ppt.txt" @@ -0,0 +1,71 @@ +进程与线程 +线程更快,轻量级,但是存在同步问题. +Thread的使用 +0.start +1.join和detach.以及不设置 +2.异常 +3.RAII +4.gei_id +5.detach 引用问题 +6.std::move +7. hardware_concurrrency() + +Data Race and Mutex +1.RAII +2.Data Race (多线程取栈元素) +3.使用Mutex保护 + +死锁 +0.多个Mutex顺序不当 +避免死锁{ + 0.使用一个mutex. + 1.持有lock的函数不再调用其他的用户函数. + 2.std::lock多个mutex + 3.lock顺序一致 +} + +unique_lock和延时初始化 +demo: open file并写数据.open file 只做一次 +写数据做多次. +open_and_write(); +0.需要线程安全,单mutex,存在打开2次的可能,使用uniq +1.lock 性能降低 +2.std::once_flag + +condition Variable +2个线程实现PV操作,难以控制sleep/while +wait虚假唤醒 +notify_one +notify_all + +std::future和async +fu.get() +std::launch方式 +promise p; +p.set(); +p.set_ex();//异常也可以设置 + +各种可以调用的函数 +std::bind +std::async +std::call_once() + +packaged_tast +回顾: +thread +mutex +lock_guard +Time + + + + + + + + + + + + + diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/Data Race.cpp" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/Data Race.cpp" new file mode 100644 index 0000000..2561fb6 --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/Data Race.cpp" @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include + +std::mutex m; +void func_1(int count){ + static int mMoney =0 ; + for(int i =0 ;i lock(m); + ++mMoney; + } + std::cout << mMoney <<" "; +} + +int main() { + + + std::thread t1(func_1,100000); + std::thread t2(func_1,200000); + std::thread t3(func_1,300000); + std::thread t4(func_1,400000); + t1.join(); + t2.join(); + t3.join(); + t4.join(); + return 0; +} diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/class_recursive_mutex.hpp" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/class_recursive_mutex.hpp" new file mode 100644 index 0000000..de05b4a --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/class_recursive_mutex.hpp" @@ -0,0 +1,35 @@ +#include +#include +#include + +class mutex { +public: + void lock(); + void unlock(); +protected: + uint32_t key{}; //bits 0-30: thread_handle, bit 31: hasWaiters_flag +}; + +class recursive_mutex : public mutex { +public: + void lock() { + uint32_t handle = current_thread_native_handle(); //obtained from TLS memory in most OS + if ((key & 0x7FFFFFFF) == handle) { // Impossible to return true unless you own the mutex. + uses++; // we own the mutex, just increase uses. + } else { + mutex::lock(); // we don't own the mutex, try to obtain it. + uses = 1; + } + } + + void unlock() { + // asserts for debug, we should own the mutex and uses> 0 + --uses; + if (uses == 0) { + mutex::unlock(); + } + } +private: + uint32_t uses{}; // no need to be atomic, can only be modified in exclusion and only interesting read is on exclusion. +}; + diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/hierarchical_mutex.cpp" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/hierarchical_mutex.cpp" new file mode 100644 index 0000000..1947772 --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/hierarchical_mutex.cpp" @@ -0,0 +1,73 @@ +#include +#include +#include + +class hierarchical_mutex { + std::mutex internal_mutex; + const unsigned long hierarchy_value; // 当前层级值 + unsigned long previous_hierarchy_value; // 前一线程的层级值 + // 所在线程的层级值,thread_local表示值存在于线程存储期 + static thread_local unsigned long this_thread_hierarchy_value; + void check_for_hierarchy_violation() { // 检查是否违反层级结构 + if (this_thread_hierarchy_value <= hierarchy_value) { + throw std::logic_error("mutex hierarchy violated"); + } + } + void update_hierarchy_value() { + // 先存储当前线程的层级值(用于解锁时恢复) + previous_hierarchy_value = this_thread_hierarchy_value; + // 再把其设为锁的层级值 + this_thread_hierarchy_value = hierarchy_value; + } + + public: + explicit hierarchical_mutex(unsigned long value) + : hierarchy_value(value), previous_hierarchy_value(0) {} + void lock() { + check_for_hierarchy_violation(); // 要求线程层级值大于锁的层级值 + internal_mutex.lock(); // 内部锁被锁住 + update_hierarchy_value(); // 更新层级值 + } + void unlock() { + if (this_thread_hierarchy_value != hierarchy_value) { + throw std::logic_error("mutex hierarchy violated"); + } + // 恢复前一线程的层级值 + this_thread_hierarchy_value = previous_hierarchy_value; + internal_mutex.unlock(); + } + bool try_lock() { + check_for_hierarchy_violation(); + if (!internal_mutex.try_lock()) return false; + update_hierarchy_value(); + return true; + } +}; + +thread_local unsigned long // 初始化为ULONG_MAX以使构造锁时能通过检查 + hierarchical_mutex::this_thread_hierarchy_value(ULONG_MAX); + +// 设定值来表示层级 +hierarchical_mutex high(10000); +hierarchical_mutex mid(6000); +hierarchical_mutex low(5000); + +void lf() { // 最低层函数 + std::scoped_lock l(low); +} + +void hf() { + std::scoped_lock l(high); + lf(); // 可以调用低层函数 +} + +void mf() { + std::scoped_lock l(mid); + hf(); // 中层调用了高层函数,违反了层次结构 +} + +using namespace std; +int main() { + hf(); // OK + mf(); // 错误 +} \ No newline at end of file diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/joining_thread.hpp" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/joining_thread.hpp" new file mode 100644 index 0000000..214a854 --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/joining_thread.hpp" @@ -0,0 +1,41 @@ +// 在std::thread基础上添加了析构函数 +#include +#include + +class joining_thread { + std::thread t; + + public: + joining_thread() noexcept = default; + + template + explicit joining_thread(T&& f, Ts&&... args) + : t(std::forward(f), std::forward(args)...) {} + + explicit joining_thread(std::thread x) noexcept : t(std::move(x)) {} + joining_thread(joining_thread&& rhs) noexcept : t(std::move(rhs.t)) {} + + joining_thread& operator=(joining_thread&& rhs) noexcept { + if (joinable()) join(); + t = std::move(rhs.t); + return *this; + } + + joining_thread& operator=(std::thread rhs) noexcept { + if (joinable()) join(); + t = std::move(rhs); + return *this; + } + + ~joining_thread() noexcept { + if (joinable()) join(); + } + + void swap(joining_thread&& rhs) noexcept { t.swap(rhs.t); } + std::thread::id get_id() const noexcept { return t.get_id(); } + bool joinable() const noexcept { return t.joinable(); } + void join() { t.join(); } + void detach() { t.detach(); } + std::thread& as_thread() noexcept { return t; } + const std::thread& as_thread() const noexcept { return t; } +}; \ No newline at end of file diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/open_source/ThreadPool-master/COPYING" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/open_source/ThreadPool-master/COPYING" new file mode 100644 index 0000000..7d69178 --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/open_source/ThreadPool-master/COPYING" @@ -0,0 +1,20 @@ +Copyright (c) 2012 Jakob Progsch, Václav Zeman + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/open_source/ThreadPool-master/README.md" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/open_source/ThreadPool-master/README.md" new file mode 100644 index 0000000..be3d25a --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/open_source/ThreadPool-master/README.md" @@ -0,0 +1,17 @@ +ThreadPool +========== + +A simple C++11 Thread Pool implementation. + +Basic usage: +```c++ +// create thread pool with 4 worker threads +ThreadPool pool(4); + +// enqueue and store future +auto result = pool.enqueue([](int answer) { return answer; }, 42); + +// get result from future +std::cout << result.get() << std::endl; + +``` diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/open_source/ThreadPool-master/ThreadPool.h" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/open_source/ThreadPool-master/ThreadPool.h" new file mode 100644 index 0000000..4183203 --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/open_source/ThreadPool-master/ThreadPool.h" @@ -0,0 +1,98 @@ +#ifndef THREAD_POOL_H +#define THREAD_POOL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class ThreadPool { +public: + ThreadPool(size_t); + template + auto enqueue(F&& f, Args&&... args) + -> std::future::type>; + ~ThreadPool(); +private: + // need to keep track of threads so we can join them + std::vector< std::thread> workers; + // the task queue + std::queue< std::function> tasks; + + // synchronization + std::mutex queue_mutex; + std::condition_variable condition; + bool stop; +}; + +// the constructor just launches some amount of workers +inline ThreadPool::ThreadPool(size_t threads) + : stop(false) +{ + for(size_t i = 0;i task; + + { + std::unique_lock lock(this->queue_mutex); + this->condition.wait(lock, + [this]{ return this->stop || !this->tasks.empty(); }); + if(this->stop && this->tasks.empty()) + return; + task = std::move(this->tasks.front()); + this->tasks.pop(); + } + + task(); + } + } + ); +} + +// add new work item to the pool +template +auto ThreadPool::enqueue(F&& f, Args&&... args) + -> std::future::type> +{ + using return_type = typename std::result_of::type; + + auto task = std::make_shared< std::packaged_task>( + std::bind(std::forward(f), std::forward(args)...) + ); + + std::future res = task->get_future(); + { + std::unique_lock lock(queue_mutex); + + // don't allow enqueueing after stopping the pool + if(stop) + throw std::runtime_error("enqueue on stopped ThreadPool"); + + tasks.emplace([task](){ (*task)(); }); + } + condition.notify_one(); + return res; +} + +// the destructor joins all threads +inline ThreadPool::~ThreadPool() +{ + { + std::unique_lock lock(queue_mutex); + stop = true; + } + condition.notify_all(); + for(std::thread &worker: workers) + worker.join(); +} + +#endif diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/open_source/ThreadPool-master/ThreadPool_example.cpp" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/open_source/ThreadPool-master/ThreadPool_example.cpp" new file mode 100644 index 0000000..837277b --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/_class/open_source/ThreadPool-master/ThreadPool_example.cpp" @@ -0,0 +1,29 @@ +#include +#include +#include + +#include "ThreadPool.h" + +int main() +{ + + ThreadPool pool(4); + std::vector< std::future> results; + + for(int i = 0; i < 8; ++i) { + results.emplace_back( + pool.enqueue([i] { + std::cout << "hello " << i << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << "world " << i << std::endl; + return i*i; + }) + ); + } + + for(auto && result: results) + std::cout << result.get() << ' '; + std::cout << std::endl; + + return 0; +} diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/apply_level/test_async.cpp" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/apply_level/test_async.cpp" new file mode 100644 index 0000000..8b21f8b --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/apply_level/test_async.cpp" @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +int f(int x){ + if (x < 0){ + throw std::out_of_range("x < 0"); + } + return x; +} + +struct A {// 成员函数 + int f(int value){return value;} + int operator()(int value){return value;} +}; + +int main(){ + std::thread t(f,1); // How to got the return value of f? + t.join(); + std::future ft = std::async(f,1);//异步任务 + std::cout << ft.get()<<" "; //可阻塞线程,并获取值 1 +// std::cout << ft.get(); //再次调用出错 + + std::future ft2 = std::async(f,-1);//异步任务 + int x = ft2.get(); // 抛出已存储的异常 + std::cout << x; + A a; + auto ft1 = std::async(&A::f, &a, 42); // 调用p->f(42),p是指向x的指针 + std::cout < +#include +#include + +int main(){ + // using namespace std::chrono; + auto startTimer = std::chrono::high_resolution_clock::now(); + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::chrono::system_clock::time_point + EndTimer = std::chrono::high_resolution_clock::now(); + std::chrono::duration fp_ms = EndTimer - startTimer; + std::cout << "duration:"<< fp_ms.count() << " ms"< +#include +#include +void f(int i) { std::cout << i< f1 = f; //存储函数 + std::function f2 = X(); //存储函数对象 + std::function f3 = [] (int i) { std::cout << i< f4 = std::bind(f, std::placeholders::_1); //存储 bind 对象 + std::function f5 = &A::f; //存储成员函数 + std::function f6 = &A::i; //存储数据成员 + + f1(1); + f2(2); + f3(3); + f4(4); + struct A a; + + f5(a,5); + f6(a); + std::cout << "f6 =" < +#include +#include +int f(int x){ + if (x < 0){ + throw std::out_of_range("x < 0"); + } + return x; +} +void task_lambda(int value){ + std::packaged_task task(f); + std::future result = task.get_future(); + task(value); //调用std::packaged_task对象,将std::future设为就绪 + std::cout << "task_lambda:\t" << result.get() << '\n'; +} +void task_future_error(){ + std::packaged_task task; + //future::get抛出future_error异常 + std::future result = task.get_future(); +} +int main(){ + // task_future_error(); + int value = 1; + task_lambda(value); + value = -1; + task_lambda(value); +} diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/apply_level/test_promise.cpp" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/apply_level/test_promise.cpp" new file mode 100644 index 0000000..7c8c64e --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/apply_level/test_promise.cpp" @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include +void test_set_promise(std::promise& promiseObj) { + std::cout << "In a thread, making data...\n"; + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + promiseObj.set_value(35); //set_value or set_value_at_thread_exit + // promiseObj.set_value_at_thread_exit(9); +} + +void test_get_promise(std::promise& promiseObj) { + std::future futureObj = promiseObj.get_future(); + futureObj.wait(); + std::cout <<" get_promise value ="<< futureObj.get() << std::endl; +} +void test_future_error() { + std::future fu; + std::cout << fu.get() << std::endl; +} + + +int main() { + std::promise promiseObj; + std::thread t1(test_get_promise, std::ref(promiseObj)); + std::thread t2(test_set_promise, std::ref(promiseObj)); + t1.join(); + t2.join(); + // test_future_error(); + + return 0; +} diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/apply_level/test_shared_future.cpp" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/apply_level/test_shared_future.cpp" new file mode 100644 index 0000000..4f21b6f --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/apply_level/test_shared_future.cpp" @@ -0,0 +1,22 @@ +#include +#include +#include +#include + +int main() +{ + std::promise promise; + std::shared_future fut(promise.get_future()); + auto fun1 = [fut](){ + fut.wait(); // waits for the signal from main() + return fut; + }; + auto fun2 = std::async(std::launch::async, fun1); + auto fun3 = std::async(std::launch::async, fun1); + auto fun4 = std::async(std::launch::async, fun1); + promise.set_value(1); + std::cout<< "fun2 wait: " << fun2.get().get() <<" "< +#include +#include + +int main() +{ + std::mutex m1; + std::mutex m2; + + auto f1 = [&]() { + std::lock_guard lg1(m1); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + std::lock_guard lg2(m2); + }; + + auto f2 = [&]() { + std::lock_guard lg1(m2); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + std::lock_guard lg2(m1); + }; + + std::thread thread1(f1); + std::thread thread2(f2); + thread1.join(); + thread2.join(); + std::cout<< "~main()" < +#include +#include + +std::mutex mtx; +void do_stuff() +{ + mtx.lock(); + // ... 做一些事 ...也可能是(抛出异常,return) + mtx.unlock(); //忘记 mtx.unlock() +} + +void do_stuff() +{ + std::unique_lock lck {mtx}; + // ... 做一些事 ...也可能是(抛出异常,return) +} + + int main(){ + std::thread st1(do_stuff); + st1.join(); +} diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/for_test_code/switch.cpp" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/for_test_code/switch.cpp" new file mode 100644 index 0000000..0d5adc6 --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/for_test_code/switch.cpp" @@ -0,0 +1,14 @@ +// 未用锁进行控制的代码 + +unsigned val; +​ +if (val < 5) { + // ... 其他线程可能在这里改动 val ... + switch (val) { + case 0: // ... + case 1: // ... + case 2: // ... + case 3: // ... + case 4: // ... + } +} diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/for_test_code/test_condition_variable_2.cpp" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/for_test_code/test_condition_variable_2.cpp" new file mode 100644 index 0000000..ff2f0cb --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/for_test_code/test_condition_variable_2.cpp" @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include +#include +#include +std::mutex mutex; +std::condition_variable cond; +std::deque queue; +int curCount = 0; +bool stop = false; +void test_emit_signal(){ + { + std::unique_lock lock(mutex); + curCount --; + queue.push_back(curCount); + }//lock.unlock(); + cond.notify_one(); +} + +void test_case(int count) +{ + for(int i = 0; i< count && (!stop) ; i++){ + test_emit_signal(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } +} +void test_wait (int StopValue = 0){ + auto thread_id = std::this_thread::get_id(); + for(;;){ + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + std::unique_lock lock(mutex); + cond.wait(lock ,[](){return stop || !queue.empty();}); + // 这一步会原子地unlock mutex 并进入blocking,不会与enqueue 死锁 + if(stop && queue.empty()){ + std::cout << "thread_id:"< +#include +#include + +class scoped_thread { + std::thread t; + + public: + explicit scoped_thread(std::thread x) : t(std::move(x)) { + if (!t.joinable()) { + throw std::logic_error("no thread"); + } + } + ~scoped_thread() { t.join(); } + scoped_thread(const scoped_thread&) = delete; + scoped_thread& operator=(const scoped_thread&) = delete; +}; + +void f() { std::cout << "OK"; } + +int main() { scoped_thread t(std::thread{f}); } diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/shared_ptr_A_B.cpp" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/shared_ptr_A_B.cpp" new file mode 100644 index 0000000..c6c7576 --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/shared_ptr_A_B.cpp" @@ -0,0 +1,26 @@ +#include +#include + +struct A; +struct B; + +struct A { + std::shared_ptr pointer; + ~A() { + std::cout << "~A" << std::endl; + } +}; +struct B { + std::weak_ptr pointer; + // std::shared_ptr pointer; + ~B() { + std::cout << "~B" << std::endl; + } +}; + +int main() { + auto a = std::make_shared(); + auto b = std::make_shared(); + a->pointer = b; + b->pointer = a; +} diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/spinlock_mutex.cpp" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/spinlock_mutex.cpp" new file mode 100644 index 0000000..1290bf6 --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/spinlock_mutex.cpp" @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +class spinlock_mutex { + std::atomic_flag flag = ATOMIC_FLAG_INIT; + + public: + void lock() { + while (flag.test_and_set(std::memory_order_acquire)) { + } + } + void unlock() { flag.clear(std::memory_order_release); } +}; + +spinlock_mutex m; + +void f(int n) { + for (int i = 0; i < 100; ++i) { + m.lock(); + std::cout << "Output from thread " << n << '\n'; + m.unlock(); + } +} + +int main() { + std::vector v; + for (int i = 0; i < 10; ++i) { + v.emplace_back(f, i); + } + for (auto& x : v) { + x.join(); + } +} \ No newline at end of file diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/test_gdb_step.txt" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/test_gdb_step.txt" new file mode 100644 index 0000000..e147ad9 --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/test_gdb_step.txt" @@ -0,0 +1,24 @@ +多线程死锁调试小技巧 +编译代码 +# g++ -Wall -std=c++11 dead_lock_demo.cpp -o dead_lock_demo -g -pthread +# ./dead_lock_demo +# ps -aux | grep dead_lock_demo + +# 使用gdb attach +# gdb attach pid + # info thread + # thread 2 + # where + # f 4 + + # thread 3 + # where + # f 4 + + +# 使用gdb core dump +# kill -11 pid +# gdb dead_lock_demo core + # thread apply all bt + # thread 2 + # f 4 \ No newline at end of file diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/test_jthread_cpp_20.cpp" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/test_jthread_cpp_20.cpp" new file mode 100644 index 0000000..f7ca0d1 --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/test_jthread_cpp_20.cpp" @@ -0,0 +1,20 @@ +#include +#include +#include +#include +//g++ -std=c++20 -pthread +void thread_proc(std::stop_token st){ + while (!st.stop_requested()){ + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + std::cout << "Thread " << std::this_thread::get_id() << " exit" << std::endl; +} + +extern "C" +int main(){ + std::jthread thr(&thread_proc); + std::this_thread::sleep_for(std::chrono::seconds(3)); + thr.request_stop(); + thr.join(); + return 0; +} diff --git "a/multiThread_notes/351円231円204円344円273円266円/test_demo/test_mutex.cpp" "b/multiThread_notes/351円231円204円344円273円266円/test_demo/test_mutex.cpp" new file mode 100644 index 0000000..e152f43 --- /dev/null +++ "b/multiThread_notes/351円231円204円344円273円266円/test_demo/test_mutex.cpp" @@ -0,0 +1,32 @@ +#include +#include