diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..6ab7bd1 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,5 @@ +{ + "default": true, + "MD013": false, + "MD033": false +} diff --git a/4_Building_Our_Own_Spin_Lock.md b/4_Building_Our_Own_Spin_Lock.md index 4dc9e11..a2ee8d9 100644 --- a/4_Building_Our_Own_Spin_Lock.md +++ b/4_Building_Our_Own_Spin_Lock.md @@ -1,5 +1,13 @@ # 第四章:构建我们自己的自旋锁 +对普通互斥锁(参见[第一章中的“锁:互斥锁和读写锁”](./1_Basic_of_Rust_Concurrency.md#锁互斥锁和读写锁))进行加锁时,如果互斥锁已经被锁定,线程将被置于睡眠状态。这避免在等待锁被释放时浪费资源。如果一个锁只会被短暂地持有,并且锁定它的线程可以在不同的处理器核心并发地运行,那么线程最好反复尝试锁定它而不实际进入睡眠态。 + +自旋锁是能够做到这一点的 mutex。试图锁定一个已经锁定的 mutex 将导致*忙碌循环*或者*自旋*:一遍又一遍的尝试。直到它成功。这可能浪费处理器周期,但有时会导致锁定时的延迟更低。 + +> 在某些平台上,许多现实世界中的 mutex 实现,包括 `std::sync::Mutex`,在告诉操作系统将线程置于睡眠状态之前,短暂地表现得像一个自旋锁。这是为了将两者的优点结合起来,尽管具体使用情况是否有益,这取决于特定的用例。 + +在该章节总,我们将建造我们自己的 `SpinLock` 类型,应用我们已经在第 [2](./2_Atomics.md) 章和第 [3](./3_Memory_Ordering.md) 章学习的,并且了解如何使用 Rust 的类型系统为我们的 SpinLock 用户提供安全且有用的接口。 + ## 一个最小实现 ## 一个不安全的自旋锁 @@ -8,6 +16,17 @@ ## 总结 +* 自旋锁是在等待时忙碌循环或自选的 mutex。 +* 自旋可以减少延迟,但也可能浪费时钟周期并降低性能。 +* 自旋循环提示(`spin::hint::spin_loop()`)可以用于通知处理器自旋循环,这可能增加它的效率。 +* `SpinLock` 只需使用 `AtomicBool` 和 `UnsafeCell` 即可实现,后者是*内部可变性*所必需的(见[第 1 章中的“内部可变性”](./1_Basic_of_Rust_Concurrency.md#内部可变性))。 +* 在解锁和锁定之间的 *happens-before 关系*是防止*数据竞争*的必要条件,否则会导致未定义行为。 +* *Acquire* 和 *Release* 内存排序对这个用例是极合适的。 +* 当做出必要的未检查的假设以避免未定义的行为时,可以通过将函数标记为不安全来将责任转移到调用者。 +* `Deref` 和 `DerefMut` trait 可用于使类型像引用一样,透明地提供对另一个对象的访问。 +* `Drop` trait 可以用于在对象被 drop 时,做一些事情,例如当它超出作用域或者它被传递给 `drop()`。 +* *锁 guard* 是一种特殊类型的有用设计模式,它被用于表示对锁定的锁的(安全)访问。由于 `Deref` trait,这种类型通常与引用的行为相似,并通过 `Drop` trait 实现自动解锁。 +

下一篇,第五章:构建我们自己的 Channel