mirror of
https://github.com/changkun/modern-cpp-tutorial.git
synced 2025-12-18 05:04:39 +03:00
book: formatting style updates (#231)
This commit is contained in:
@@ -70,7 +70,7 @@ int main() {
|
||||
由于 C++ 保证了所有栈对象在生命周期结束时会被销毁,所以这样的代码也是异常安全的。
|
||||
无论 `critical_section()` 正常返回、还是在中途抛出异常,都会引发堆栈回退,也就自动调用了 `unlock()`。
|
||||
|
||||
而 `std::unique_lock` 则相对于 `std::lock_guard` 出现的,`std::unique_lock` 更加灵活,
|
||||
而 `std::unique_lock` 则是相对于 `std::lock_guard` 出现的,`std::unique_lock` 更加灵活,
|
||||
`std::unique_lock` 的对象会以独占所有权(没有其他的 `unique_lock` 对象同时拥有某个 `mutex` 对象的所有权)
|
||||
的方式管理 `mutex` 对象上的上锁和解锁的操作。所以在并发编程中,推荐使用 `std::unique_lock`。
|
||||
|
||||
@@ -147,7 +147,8 @@ int main() {
|
||||
std::cout << "waiting...";
|
||||
result.wait(); // 在此设置屏障,阻塞到期物的完成
|
||||
// 输出执行结果
|
||||
std::cout << "done!" << std:: endl << "future result is " << result.get() << std::endl;
|
||||
std::cout << "done!" << std:: endl << "future result is "
|
||||
<< result.get() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
@@ -198,7 +199,8 @@ int main() {
|
||||
}
|
||||
// 短暂取消锁,使得生产者有机会在消费者消费空前继续生产
|
||||
lock.unlock();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 消费者慢于生产者
|
||||
// 消费者慢于生产者
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
lock.lock();
|
||||
while (!produced_nums.empty()) {
|
||||
std::cout << "consuming " << produced_nums.front() << std::endl;
|
||||
@@ -277,7 +279,7 @@ int main() {
|
||||
这是一组非常强的同步条件,换句话说当最终编译为 CPU 指令时会表现为非常多的指令(我们之后再来看如何实现一个简单的互斥锁)。
|
||||
这对于一个仅需原子级操作(没有中间态)的变量,似乎太苛刻了。
|
||||
|
||||
关于同步条件的研究有着非常久远的历史,我们在这里不进行赘述。读者应该明白,在现代 CPU 体系结构下提供了 CPU 指令级的原子操作,
|
||||
关于同步条件的研究有着非常久远的历史,我们在这里不进行赘述。读者应该明白,现代 CPU 体系结构提供了 CPU 指令级的原子操作,
|
||||
因此在 C++11 中多线程下共享变量的读写这一问题上,还引入了 `std::atomic` 模板,使得我们实例化一个原子类型,将一个
|
||||
原子类型读写操作从一组指令,最小化到单个 CPU 指令。例如:
|
||||
|
||||
@@ -311,7 +313,7 @@ int main() {
|
||||
}
|
||||
```
|
||||
|
||||
当然,并非所有的类型都能提供原子操作,这是因为原子操作的可行性取决于 CPU 的架构以及所实例化的类型结构是否满足该架构对内存对齐
|
||||
当然,并非所有的类型都能提供原子操作,这是因为原子操作的可行性取决于具体的 CPU 架构,以及所实例化的类型结构是否能够满足该 CPU 架构对内存对齐
|
||||
条件的要求,因而我们总是可以通过 `std::atomic<T>::is_lock_free` 来检查该原子类型是否需支持原子操作,例如:
|
||||
|
||||
```cpp
|
||||
@@ -419,7 +421,7 @@ int main() {
|
||||
|
||||
|
||||
T2 ---------+------------+--------------------+--------+-------->
|
||||
x.read() x.read() x.read() x.read()
|
||||
x.read x.read() x.read() x.read()
|
||||
```
|
||||
|
||||
在上面的情况中,如果我们假设 x 的初始值为 0,则 `T2` 中四次 `x.read()` 结果可能但不限于以下情况:
|
||||
@@ -428,7 +430,8 @@ int main() {
|
||||
3 4 4 4 // x 的写操作被很快观察到
|
||||
0 3 3 4 // x 的写操作被观察到的时间存在一定延迟
|
||||
0 0 0 4 // 最后一次读操作读到了 x 的最终值,但此前的变化并未观察到
|
||||
0 0 0 0 // 在当前时间段内 x 的写操作均未被观察到,但未来某个时间点上一定能观察到 x 为 4 的情况
|
||||
0 0 0 0 // 在当前时间段内 x 的写操作均未被观察到,
|
||||
// 但未来某个时间点上一定能观察到 x 为 4 的情况
|
||||
```
|
||||
|
||||
### 内存顺序
|
||||
@@ -489,9 +492,8 @@ int main() {
|
||||
});
|
||||
std::thread acqrel([&]() {
|
||||
int expected = 1; // must before compare_exchange_strong
|
||||
while(!flag.compare_exchange_strong(expected, 2, std::memory_order_acq_rel)) {
|
||||
while(!flag.compare_exchange_strong(expected, 2, std::memory_order_acq_rel))
|
||||
expected = 1; // must after compare_exchange_strong
|
||||
}
|
||||
// flag has changed to 2
|
||||
});
|
||||
std::thread acquire([&]() {
|
||||
@@ -553,7 +555,7 @@ C++11 语言层提供了并发编程的相关支持,本节简单的介绍了 `
|
||||
|
||||
## 进一步阅读的参考资料
|
||||
|
||||
- [C++ 并发编程\(中文版\)](https://www.gitbook.com/book/chenxiaowei/cpp_concurrency_in_action/details)
|
||||
- [C++ 并发编程\(中文版\)](https://book.douban.com/subject/26386925/)
|
||||
- [线程支持库文档](https://en.cppreference.com/w/cpp/thread)
|
||||
- Herlihy, M. P., & Wing, J. M. (1990). Linearizability: a correctness condition for concurrent objects. ACM Transactions on Programming Languages and Systems, 12(3), 463–492. https://doi.org/10.1145/78969.78972
|
||||
|
||||
|
||||
Reference in New Issue
Block a user