From a2cfd4e78545e8861abce839148cfc49470f64d4 Mon Sep 17 00:00:00 2001 From: Liuyuan <96400967+LiuYuan-SHU@users.noreply.github.com> Date: Sat, 31 Dec 2022 21:54:47 +0800 Subject: [PATCH] doc: update cpp.md (#255) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 更新排版 更新了以下内容的排版,以适应网页的风格: + lambda表达式 + 多线程 * 更新C++多线程:锁 更新以下部分: + `mutex` + 上锁——`lock` + 解锁——`unlock` + 尝试上锁——`try_lock` + `lock_guard` + 额外参数`std::adopt_lock` + `unique_lock` + 额外参数 + `std::adopt_lock` + `std::try_to_lock` + `std::defer_lock` + 成员函数 + `lock` + `unlock` + `release` + `owns_lock` * 更新C++11多线程内容 更新内容: + `call_once` + `condition_variable` + `wait` + `notify_one` + `notify_all` + `async` + `std::launch::deferred` + `std::launch::async` + `std::deferred | std::launch::async` + `future` + `wait` + `get` + `wait_for` + `wait_until` + `future_status` + `ready` + `timeout` + `deferred` + `shared_future` --- docs/cpp.md | 326 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 294 insertions(+), 32 deletions(-) diff --git a/docs/cpp.md b/docs/cpp.md index a17d654..0ac68ba 100644 --- a/docs/cpp.md +++ b/docs/cpp.md @@ -602,12 +602,14 @@ auto func = []() -> return_type { }; auto func1 = [=, &str1]() -> int { - return val1 == std::stoi(str1) ? val1 : val2; + return val1 == std::stoi(str1) + ? val1 : val2; }; auto func2 = [&, val1]() -> int { - return str1 == std::to_string(val1) ? str1 : str2; + return str1 == std::to_string(val1) + ? str1 : str2; }; ``` @@ -619,38 +621,41 @@ auto func = []() -> return_type { }; 此处给出一个 Lambda 表达式的实际使用例子(当然可以使用 `str::copy`): ```cpp -std::vector vec({1, 2, 3, 4, 5}); // vec中包含1, 2, 3, 4, 5 -std::for_each(vec.begin(), vec.end(), [](int& ele) -> void - { - std::cout << ele << " "; - }); +// vec中包含1, 2, 3, 4, 5 +std::vector vec({1, 2, 3, 4, 5}); +std::for_each(vec.begin(), vec.end(), + [](int& ele) -> void + { + std::cout << ele + << " "; + }); ``` ## C++多线程 -### 多线程 介绍 - -g++编译选项:`std=c++11`,包含头文件: - -- `#include `:C++多线程库 -- `#include `:C++互斥量库 +> g++编译选项:`-std=c++11` +> +> 包含头文件: +> +> + `#include `:C++多线程库 +> + `#include `:C++互斥量库 +> + `#include `:C++异步库 ### 线程的创建 - 以普通函数作为线程入口函数: -```cpp -void thread_entry_function_1() { } -void thread_entry_function_2(int val) { } +```c++ +void entry_1() { } +void entry_2(int val) { } -std::thread my_thread_1(thread_entry_function_1); -std::thread my_thread_2(thread_entry_function_2, 5); +std::thread my_thread_1(entry_1); +std::thread my_thread_2(entry_2, 5); ``` 以类对象作为线程入口函数: -```cpp +```c++ class Entry { void operator()() { } @@ -659,23 +664,23 @@ class Entry Entry entry; // 调用operator()() -std::thread my_thread_1(entry); +std::thread my_thread_1(entry); // 调用Entry::entry_function -std::thread my_thread_2(&Entry::entry_function, &entry); +std::thread my_thread_2(&Entry::entry_function, &entry); ``` 以lambda表达式作为线程入口函数: -```cpp +```c++ std::thread my_thread([]() -> void - { - // ... - }); + { + // ... + }); ``` ### 线程的销毁 -```cpp +```c++ thread my_thread; // 阻塞 my_thread.join(); @@ -685,13 +690,270 @@ my_thread.detach(); ### `this_thread` -```cpp -std::this_thread::get_id(); // 获取当前线程ID -std::this_thread::sleep_for(); // 使当前线程休眠一段指定时间 -std::this_thread::sleep_until();// 使当前线程休眠到指定时间 -std::this_thread::yield(); // 暂停当前线程的执行,让别的线程执行 +```c++ +// 获取当前线程ID +std::this_thread::get_id(); +// 使当前线程休眠一段指定时间 +std::this_thread::sleep_for(); +// 使当前线程休眠到指定时间 +std::this_thread::sleep_until(); +// 暂停当前线程的执行,让别的线程执行 +std::this_thread::yield(); ``` +### 锁 + +> `#include ` + +#### 锁的基本操作 + +创建锁 + +```c++ +std::mutex m; +``` + +上锁 + +```c++ +m.lock(); +``` + +解锁 + +```c++ +m.unlock(); +``` + +尝试上锁:成功返回`true`,失败返回`false` + +```c++ +m.try_lock(); +``` + +解锁 + +```c++ +m.unlock(); +``` + +#### 更简单的锁——`std::lock_guard` + +构造时上锁,析构时解锁 + +```c++ +std::mutex m; +std::lock_guard lock(m); +``` + +额外参数:`std::adopt_lock`:只需解锁,无需上锁 + +```c++ +// 手动上锁 +m.lock(); +std::lock_guard lock(m, + std::adopt_lock); +``` + +#### `unique_lock` + +构造上锁,析构解锁 + +```c++ +std::mutex m; +std::unique_lock lock(m); +``` + +##### `std::adopt_lock` + +只需解锁,无需上锁 + +```c++ +// 手动上锁 +m.lock(); +std::unique_lock lock(m, + std::adopt_lock); +``` + +##### `std::try_to_lock` + +尝试上锁,可以通过`std::unique_lock::owns_lock()`查看状态 + +```c++ +std::unique_lock lock(m, + std::try_to_lock); +if (lock.owns_lock()) +{ + // 拿到了锁 +} +else +{ + // 没有 +} +``` + +##### `std::defer_lock` + +绑定锁,但不上锁 + +```c++ +std::unique_lock lock(m, + std::defer_lock); +lock.lock(); +lock.unlock(); +``` + +##### `std::unique_lock::release` + +返回所管理的`mutex`对象指针,**释放所有权。**一旦释放了所有权,那么如果原来互斥量处于互斥状态,程序员有责任手动解锁。 + +#### `std::call_once` + +当多个线程通过这个函数调用一个可调用对象时,只会有一个线程成功调用。 + +```c++ +std::once_flag flag; + +void foo() { } + +std::call_once(flag, foo); +``` + +### `std::condition_variable` + +#### 创建条件变量 + +```c++ +std::condition_variable cond; +``` + +#### 等待条件变量被通知 + +```c++ +std::unique_lock + lock; +extern bool predicate(); + +// 调用方式 1 +cond.wait(lock); +// 调用方式 2 +cond.wait(lock, predicate); +``` + +1. `wait`不断地尝试重新获取并加锁该互斥量,如果获取不到,它就卡在这里并反复尝试重新获取,如果获取到了,执行流程就继续往下走 +2. `wait`在获取到互斥量并加锁了互斥量之后: + 1. 如果`wait`被提供了可调用对象,那么就执行这个可调用对象: + + 如果返回值为`false`,那么`wait`继续加锁,直到再次被notified + + 如果返回值为`true`,那么`wait`返回,继续执行流程 + 2. 如果`wait`没有第二个参数,那么直接返回,继续执行 + +#### `std::condition_variable::notify_one` + +`notify_one`唤醒一个调用`wait`的线程。注意在唤醒之前要解锁,否则调用`wait`的线程也会因为无法加锁而阻塞。 + +#### `std::condition_variable::notify_all` + +唤醒所有调用`wait`的线程。 + +### 获取线程的运行结果 + +> `#include ` + +#### 创建异步任务 + +```c++ +double func(int val); + +// 使用std::async创建异步任务 +// 使用std::future获取结果 +// future模板中存放返回值类型 +std::future result = + std::async(func, 5); +``` + +#### 获取异步任务的返回值 + +等待异步任务结束,但是不获取返回值: + +```c++ +result.wait(); +``` + +获取异步任务的返回值: + +```c++ +int val = result.get(); +``` + +注: + ++ `get()`返回右值,因此只可调用一次 ++ 只要调用上述任意函数,线程就会一直阻塞到返回值可用(入口函数运行结束) + +#### `std::async`的额外参数 + +额外参数可以被放在`std::async`的第一个参数位置,用于设定`std::async`的行为: + +1. `std::launch::deferred`:入口函数的运行会被推迟到`std::future::get()`或者`std::future::wait()`被调用时。此时调用线程会直接运行线程入口函数,换言之,**不会创建子线程** +2. `std::launch::async`:立即创建子线程,并运行线程入口函数 +3. `std::launch::deferred | std::launch::async`:默认值,由系统自行决定 + +#### 返回值的状态 + +让当前线程等待一段时间(等待到指定时间点),以期待返回值准备好: + +```c++ +extern double foo(int val) {} + +std::future result = + async(foo, 5); + +//返回值类型 +std::future_status status; +// 等待一段时间 +status = result.wait_for( + std::chrono::seconds(1) + ); +// 等待到某一时间点 +status = result.wait_for( + std::chrono::now() + + std::chrono::seconds(1) + ); +``` + +在指定的时间过去后,可以获取等待的结果: + +```c++ +// 返回值已经准备好 +if (status == + std::future_status::ready) +{ + +} +// 超时:尚未准备好 +else if (status == + std::future_status::timeout) +{ + +} +// 尚未启动: std::launch::deferred +else if (status == + std::future_status::deferred) +{ + +} +``` + +#### 多个返回值 + +```c++ +std::shared_future result; +``` + +如果要多次获取结果,可以使用`std::shared_future`,其会返回结果的一个**拷贝**。 + +对于不可拷贝对象,可以在`std::shared_future`中存储对象的指针,而非指针本身。 + C++ 预处理器 ------------