为什么没有 std:: 等价于 pthread_spinlock_t 就像 pthread_mutex_t & std::mutex 一样?

use*_*066 4 c++ linux multithreading c++11

我已经将 pthreads 用于并发程序,主要使用自旋锁、互斥锁和条件变量。

我开始研究使用 std::thread 和 std::mutex 的多线程,我注意到 pthread 中似乎没有与自旋锁等效的东西。

有谁知道这是为什么?

Max*_*kin 8

pthreads 中似乎没有与自旋锁等效的东西。

自旋锁在用户空间中通常被认为是错误的工具,因为在持有自旋锁时无法禁用线程抢占(与内核中不同)。这样一个线程可以获取自旋锁然后被抢占,导致所有其他尝试获取自旋锁的线程不必要地自旋(如果这些线程具有更高的优先级,可能会导致死锁(等待 I/O 的线程可能会获得优先级)唤醒时增强))。这个推理也适用于所有无锁数据结构,除非数据结构是真正的无等待(除了 ,没有很多实际有用的boost::spsc_queue)。

在内核中,锁定自旋锁的线程在释放自旋锁之前不能被抢占或中断。这就是为什么自旋锁在那里是合适的(当RCU不能使用时)。

在 Linux 上,可以通过使用隔离的 CPU 内核和固定到这些隔离内核的 FIFO 实时线程来防止抢占(不确定是否完全,但最近内核已朝着这种理想的效果进行了更改)。但这需要经过深思熟虑的内核/机器配置以及旨在利用该配置的应用程序。尽管如此,人们确实将这样的设置用于关键业务应用程序以及用户空间中的无锁(但不是无等待)数据结构。


在 Linux 上,有自适应互斥锁PTHREAD_MUTEX_ADAPTIVE_NP,它在内核阻塞之前旋转有限次数的迭代(类似于InitializeCriticalSectionAndSpinCount)。但是,该互斥锁不能通过std::mutex接口使用,因为pthread_mutexattr_t在初始化之前没有自定义不可移植的选项pthread_mutex_t

既不能通过std::mutex接口启用进程共享、稳健性、错误检查或优先级反转预防。在实践中,人们编写自己的包装器pthread_mutex_t,允许设置所需的互斥锁属性;以及相应的条件变量包装器。标准锁喜欢std::unique_lock并且std::lock_guard可以重复使用。

IMO,可能会规定在std::API 中设置所需的互斥锁和条件变量属性,例如protected为派生类提供一个构造函数来初始化它native_handle,但没有。这native_handle看起来像一个好主意,做平台具体的东西,但是,必须有一个构造派生类能够适当地初始化。在互斥锁或条件变量初始化之后,这native_handle几乎是无用的。除非这个想法只是能够将它传递native_handle给(C 语言)API,这些 API 需要一个指针或对初始化的pthread_mutex_t.


还有一个 Boost/C++ 标准不接受信号量的例子,因为信号量太大了,不能自挂,互斥量(本质上是一个二进制信号量)和条件变量是更基本和更灵活的同步原语,out其中可以构建一个信号量

从 C++ 标准的角度来看,这些可能是正确的决定,因为教育用户正确使用自旋锁和信号量是一项艰巨的任务。而高级用户可以毫不费力地制作包装器pthread_spinlock_t