> For the complete documentation index, see [llms.txt](https://chenxiaowei.gitbook.io/concurrency-with-modern-c/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://chenxiaowei.gitbook.io/concurrency-with-modern-c/geng-duo-xin-xi/11.4-chinese.md).

# 术语表

本术语表只为基本术语提供参考。

## ACID

事务具有原子性、一致性、隔离性和持久性(ACID)属性的操作。在C++中，除了持久性之外，事务性内存的所有属性都保持不变。

* 原子性：执行或不执行块的所有语句。
* 一致性：系统始终处于一致的状态，所有事务构建顺序一致。
* 独立性：每个事务在完全隔离的情况下运行。
* 会对事务的持久性进行记录。

## CAS

CAS表示compare-and-swap，是一个原子操作。它将内存位置与给定值进行比较，如果内存位置与给定值相同，则修改内存位置的值。在C++中，CAS操作有`std::compare_exchange_strong`和`std::compare_exchange_weak`。

## 可调用单元

可调用单元的行为类似于函数。不仅是函数，还有函数对象和Lambda函数。如果一个可调用单元接受一个参数，它就被称为一元可调用单元；如果有两个参数，就是二元可调用单元。

谓词是返回布尔值的特殊可调用项。

## 并发性

并发性意味着多个任务的重叠执行。而且，并发是并行的超集。

## 临界区

临界区是一段代码，最多只有一个线程可以访问。

## 立即求值

如果立即求值，则立即求出表达式的值，则该策略与延迟求值正交。立即求值通常也称为贪婪求值。

## Executor

执行者是与特定执行上下文相关联的对象。它提供一个或多个执行函数，用于为可调用的函数对象创建执行代理。

## 函数对象

首先，不要叫它们[函子](https://en.wikipedia.org/wiki/Functor)。这是一个明确的数学术语，叫做[范畴理论](https://en.wikipedia.org/wiki/Category_theory)。

函数对象是行为类似于函数，通过实现函数调用操作符来实现这一点。由于函数对象是对象，因此可以有属性和状态。

```cpp
struct Square{
    void operator()(int& i){i= i*i;}
};

std::vector<int> myVec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

std::for_each(myVec.begin(), myVec.end(), Square());

for (auto v: myVec) std::cout << v << " "; // 1 4 9 16 25 36 49 64 81 100
```

> **实例化函数对象**
>
> 常见的错误是在算法中使用函数对象(`Square`)的名称，而不是函数对象(`Square()`)本身的实例，比如：`std::for_each(myVec.begin()， myVec.end()， Square)`，应该使用：`std::for_each(myVec.begin()， myVec.end()， Square())`。

## Lambda函数

Lambda函数可以就地提供需要的功能，编译器当场就能得到相应的信息，因此具有极佳的优化潜力。Lambda函数可以通过值或引用来接收它们的参数，还可以通过值或引用捕获已定义的变量。

```cpp
std::vector<int> myVec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::for_each(myVec.begin(), myVec.end(), [](int& i){ i= i*i; });
// 1 4 9 16 25 36 49 64 81 100
```

> **应该首选Lambda函数**
>
> 如果可调用的功能是简短和可以自解释的，使用Lambda函数最好不过。Lambda函数通常比函数或函数对象更快，而且更容易理解。

## 延迟求值

[延迟求值](https://en.wikipedia.org/wiki/Lazy_evaluation)的情况下，仅在需要时才对表达式求值。该策略与立即求值策略正交。延迟求值通常称为按需调用。

## 无锁

如果保证了系统范围内的进程无影响，那么非阻塞算法就是无锁的。

## 未唤醒

未唤醒是指，线程由于竞争条件而丢失唤醒通知的情况。

如果使用没有使用谓词，可能会发生这种情况。

## 数学规律

某个集合X上的一个二进制操作(\*)：

* 结合律，满足x, y, z中的所有x, y, z的结合律：(x *y)* z = x *(y* z)
* 交换律，满足所有x和y的交换律x *y = y* x

## 内存位置

内存位置的详解可以参考[cppreference.com](http://en.cppreference.com/w/cpp/language/memory_model)

* 标量类型的对象(算术类型、指针类型、枚举类型或`std::nullptr_t`。
* 非零长度的最大连续序列。

## 内存模型

内存模型定义了对象和内存位置之间的关系，特别是处理了以下问题：如果两个线程访问相同的内存位置，会发生什么情况。

## 修改顺序

对特定原子对象M的所有修改，都以特定的顺序进行，这个顺序称为M的修改顺序。因此，线程读取原子对象时，不会看到比线程已经观察到的值更“旧”的值。

## Monad(单子)

Haskell作为一种纯函数语言，只有纯函数。这些纯函数的一个关键特性，当给定相同的参数时，总是返回相同的结果。有了这个[透明参照](https://en.wikipedia.org/wiki/Referential_transparency)的属性，Haskell函数才不会有副作用。因此，Haskell有一个概念上的问题。到处都是有副作用的计算，这些计算可能会失败，可能返回未知数量的结果，或者依赖于环境。为了解决这个概念上的问题，Haskell使用单子并将它们嵌入到纯函数语言中。

经典的单子封装：

* I/O单子：计算输入和输出的结果。
* 可能性单子：可能会返回计算结果的单子。
* 错误单子：计算可能失败。
* 列表单子：计算可以有任意数量的结果。
* 状态单子：基于状态的计算。
* 读者单子：基于环境的计算。

单子的概念来自数学中的[范畴理论](https://en.wikipedia.org/wiki/Category_theory)，其处理对象之间的映射。单子是抽象的数据类型，将简单的类型转换为丰富的类型。这些丰富类型的值称为一元值。当进入单子，一个值只能由一个函数组合转换成另一个一元值。

这种组合尊重了单子的独特结构。因此，当发生错误，错误单子中断它的计算，或重新构建状态单子的状态。

一个单子包括三个部分:

* 类型构造函数：定义简单数据类型，如何成为一元数据类型。
* 函数:
  * 恒等函数：在单子中引入一个简单的值。
  * 绑定操作符：定义如何将函数应用于一元值，以获得新的一元值。
* 功能规则:
  * 恒等函数的左右必须是恒等元素。
  * 函数的复合必须遵循结合律。

要使错误单子成为类型类单子的实例，错误单子必须支持恒等函数和绑定操作符，这两个函数定义了错误单子应该如何处理计算中的错误。如果使用错误单子，错误处理会在后台完成。

单子由两个控制流组成：用于计算结果的显式控制和用于处理特定副作用的隐式控制流。

当然，也可以用更少的词来定义单子：“单子只是内函子类中的一个独异点(monoid)。”

单子在C++中变得越来越重要。在C++ 17中，添加了[`std::optional`](http://en.cppreference.com/w/cpp/utility/optional) ，这是一种可能性单子。在C++20/23中，可能会从Eric Niebler那里得到扩展future和[范围库](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4128.html)，二者也都是单子。

## 无阻塞

如果任何线程的失败或挂起，不会导致另一个线程的失败或挂起，则称为非阻塞。这个定义来自于[《Java并发实践》](http://jcip.net/)。

## 并行性

并行性意味着同时执行多个任务。并行性是并发性的一个子集。

## 谓词

谓词是返回布尔值的可调用单元。如果一个谓词有一个参数，它就称为一元谓词。如果一个谓词有两个参数，就称为二元谓词。

## 模式

“每个模式规则都是一个由三部分组成，表明了特定上下文、问题和解决方案之间的关系。“ —— [Christopher Alexander](https://en.wikipedia.org/wiki/Christopher_Alexander)

## RAII

资源获取是初始化(RAII)，代表C++中的一种流行技术，在这种技术中，资源的获取和释放与对象的生命周期绑定在一起。这意味着对于锁，互斥锁将被锁定在构造函数中，并在析构函数中解锁。这种RAII实现，也称为范围锁定。

C++中的典型用例有：管理互斥锁生命周期的锁、管理资源(内存)生命周期的智能指针，或者管理元素生命周期的[标准模板库容器](https://en.cppreference.com/w/cpp/container)。

## 释放序列

原子对象M的释放序列，以释放操作A为首，是M修改顺序中最大的连续子序列，其中第一个操作为A，每个后续操作为:

* 由执行A操作的线程进行的操作
* 原子的读-改-写操作。

## 顺序一致的存储模型

顺序一致有两个基本特征:

1. 程序的指令是按源代码顺序执行的。
2. 所有线程上的所有操作都遵循全局顺序。

## 序列点

序列点定义了程序执行过程中的任何一个结点。在这个点上，可以保证先前评估的所有执行效果，而不影响后续评估的 执行效果。

## 伪唤醒

伪唤醒是一种条件变量的现象。可能发生的情况是，条件变量的等待组件错误地获取了一个通知。

## 线程

计算机科学中，执行线程是可由调度器独立管理的最小程序指令序列，调度器通常是操作系统的一部分。线程和进程的实现在不同的操作系统之间是不同的，但是在大多数情况下，线程是进程的一个组件。多个线程可以存放在于一个进程中，并发执行并共享内存等资源，而不同的进程不共享这些资源。特别是，进程中的线程在任何给定时间，共享其可执行代码和变量。想要了解更多信息，可以阅读维基百科关于\[线程]\(<https://en.wikipedia.org/wiki/Thread_(computing))的文章。>

## 全序关系

总序是一个二元关系(<=)在某个集合X上表现，其有反对称性、传递性，完全性。

* 反对称性：如果a <= b并且b <= a，则a == b
* 传递性：如果a <= b, b <= c，则a <= c
* 完全性：a <= b或b <= a

## volatile

volatile通常用于表示可以独立于常规程序流进行更改的对象。例如，这些对象在嵌入式编程中表示一个外部设备(内存映射I/O)。由于这些对象可以独立于常规程序流进行更改，并且其值可以直接写入主内存，因此不会在缓存中进行优化存储。

## 无等待

当有每个线程都有进程保证不会互相影响时，那么一个非阻塞算法是无等待的。
