Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 63bbb99

Browse files
committed
1. 修改第一章与第二章小部分措辞与格式
2. 完成部分第四章内容,开始正文
1 parent 5ca44a5 commit 63bbb99

File tree

3 files changed

+53
-3
lines changed

3 files changed

+53
-3
lines changed

‎md/01基本概念.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
  在我们谈起"*并发编程*",其实可以直接简单理解为"**多线程编程**",我知道你或许有疑问:"那多进程呢?" C++ 语言层面没有进程的概念,并发支持库也不涉及多进程,所以在本教程中,不用在意。
66

7-
  我们完全使用标准 C++ 进行教学。
7+
  我们主要使用标准 C++ 进行教学,也会稍微涉及一些其它库
88

99
## 并发
1010

‎md/04同步操作.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# 同步操作
22

3-
"同步操作"是指在计算机科学和信息技术中的一种操作方式,其中不同的任务或操作按顺序执行,一个操作完成后才能开始下一个操作。在同步操作中,各个任务之间通常需要相互**协调和等待**,以确保**数据的一致性和正确性**
3+
"同步操作"是指在计算机科学和信息技术中的一种操作方式,其中不同的任务或操作按顺序执行,一个操作完成后才能开始下一个操作。在多线程编程中,各个任务通常需要通过同步操作进行相互**协调和等待**,以确保数据的**一致性****正确性**
44

55
本章的主要内容有:
66

@@ -1120,6 +1120,6 @@ C++11 的 `std::this_thread::get_id()` 返回的内部类型没办法直接转
11201120
11211121
## 总结
11221122
1123-
在并发编程中,同步操作对于并发编程至关重要。如果没有同步,线程基本上就是独立的,因其任务之间的相关性,才可作为一个整体执行(比如第二章的并行求和)。本章讨论了多种用于同步操作的工具,包括条件变量、future、promise、package_task。同时,详细介绍了 C++ 时间库的知识,以使用并发支持库中的"限时等待"。最后使用 CMake + Qt 构建了一个带有 UI 界面的示例,展示异步多线程的必要性
1123+
在并发编程中,同步操作对于并发编程至关重要。如果没有同步,线程基本上就是独立的,因其任务之间的相关性,才可作为一个整体执行(比如第二章的并行求和)。本章讨论了多种用于同步操作的工具,包括条件变量、future、promise、package_task。同时,详细介绍了 C++ 时间库的知识,以使用并发支持库中的"限时等待"。最后使用 CMake + Qt 构建了一个带有 UI 界面的示例,展示异步多线程的**必要性**
11241124
11251125
在讨论了 C++ 中的高级工具之后,现在让我们来看看底层工具:C++ 内存模型与原子操作。

‎md/05内存模型与原子操作.md‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,51 @@
11
# 内存模型与原子操作
2+
3+
- 内存模型定义了多线程程序中,读写操作如何在不同线程之间可见,以及这些操作在何种顺序下执行。内存模型确保程序的行为在并发环境下是可预测的。
4+
5+
- 原子操作即**不可分割的操作**。系统的所有线程,不可能观察到原子操作完成了一半。
6+
7+
最基础的概念就是如此,这里不再过多赘述,后续还会详细展开内存模型的问题。
8+
9+
## 原子操作
10+
11+
```cpp
12+
int a = 0;
13+
void f(){
14+
++a;
15+
}
16+
```
17+
18+
显然,`++a` 是非原子操作,也就是说在多线程中可能会被另一个线程观察到只完成一半。
19+
20+
1. 线程 A 和线程 B 同时开始修改变量 `a` 的值。
21+
2. 线程 A 对 `a` 执行递增操作,但还未完成。
22+
3. 在线程 A 完成递增操作之前,线程 B 也执行了递增操作。
23+
4. 线程 C 读取 `a` 的值。
24+
25+
线程 C 到底读取到多少不确定,a 的值是多少也不确定。显然,这构成了数据竞争,出现了[未定义行为](https://zh.cppreference.com/w/cpp/language/ub)
26+
27+
在之前的内容中,我们讲述了使用很多设施,如互斥量,来保护共享资源。
28+
29+
```cpp
30+
std::mutex m;
31+
void f() {
32+
std::lock_guard<std::mutex>{m};
33+
++a;
34+
}
35+
```
36+
37+
通过互斥量的保护,即使 `++a` 本身不是原子操作,**逻辑上也可视为原子操作**。互斥量确保了对共享资源的访问是线程安全的,避免了数据竞争问题。
38+
39+
不过这显然不是我们的重点,C++11 引入了原子类型 [`std::atomic`](https://zh.cppreference.com/w/cpp/atomic/atomic),在下文我们会详细讲解。
40+
41+
### 原子类型 `std::atomic`
42+
43+
标准原子类型定义在头文件 `<atomic>` 中。这些类型的操作都是原子的,语言定义中只有这些类型的操作是原子的,虽然也可以用互斥量来模拟原子操作(见上文)。标准的原子的类型实现可能是:*它们几乎都有一个 `is_lock_free()` 成员函数,这个函数可以让用户查询某原子类型的操作是直接用的原子指令(返回 `true`),还是内部用了锁实现(返回 `false`)。*
44+
45+
原子操作可以代替互斥量,来进行同步操作,也能带来更高的性能。但是如果它的内部使用互斥量实现,那么不可能有性能的提升。
46+
47+
在 C++17 中,所有原子类型都有一个 `static constexpr` 的数据成员 [`is_always_lock_free`](https://zh.cppreference.com/w/cpp/atomic/atomic/is_always_lock_free) 。如果当前环境上的原子类型 X 是无锁类型,那么 `X::is_always_lock_free` 将返回 `true` 。例如:
48+
49+
```cpp
50+
std::atomic<int>::is_always_lock_free // true 或 false
51+
```

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /