Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 互斥锁如何在低级别等待解锁?_C++_Multithreading_Locking_Mutex - Fatal编程技术网

C++ 互斥锁如何在低级别等待解锁?

C++ 互斥锁如何在低级别等待解锁?,c++,multithreading,locking,mutex,C++,Multithreading,Locking,Mutex,我想知道互斥锁(或其他锁定实现)如何实现锁定函数的等待功能。我的意思是,这是一条cpu指令,对mutex.lock调用进行排队,这是仅在操作系统中实现的还是什么 在我所做的测试中,我认为这种等待功能只在操作系统层完成,并且创建了某种旋转,检查锁是否可以继续,如果没有使线程进入睡眠状态。是这样吗 非常感谢。这取决于平台。通常,如果达到固定的自旋限制,操作系统中会有一个自旋锁部分返回到阻塞状态 自旋锁通常通过在互斥锁解锁时读取包含特定值的内存地址来实现。如果它被视为已解锁,则会尝试以原子方式将该值从

我想知道互斥锁(或其他锁定实现)如何实现锁定函数的等待功能。我的意思是,这是一条cpu指令,对mutex.lock调用进行排队,这是仅在操作系统中实现的还是什么

在我所做的测试中,我认为这种等待功能只在操作系统层完成,并且创建了某种旋转,检查锁是否可以继续,如果没有使线程进入睡眠状态。是这样吗


非常感谢。

这取决于平台。通常,如果达到固定的自旋限制,操作系统中会有一个自旋锁部分返回到阻塞状态

自旋锁通常通过在互斥锁解锁时读取包含特定值的内存地址来实现。如果它被视为已解锁,则会尝试以原子方式将该值从解锁值更改为锁定值。如果原子交换成功,互斥锁将被锁定。通常会计算旋转的数量,如果达到限制,我们会切换到操作系统中的阻塞

操作系统中的块的实现方式基本相同,只是线程将自己添加到等待锁定的事物列表中,而不是休眠。当一个线程释放锁时,它会检查操作系统中是否有任何东西在等待,如果有,就会解除锁定。这会导致操作系统调度该线程。然后,它通常会尝试执行与自旋锁相同的原子交换,如果失败,会在操作系统中再次阻塞

在伪代码中:

锁定

  • 检查内存位置,查看锁是否已锁定。如果是,请转至步骤3
  • 尝试以原子方式将内存位置从解锁切换到锁定。如果我们成功了,停下来,我们就握紧了锁
  • 增加旋转计数。如果我们没有旋转太多次,请转至步骤1
  • 自动增加等待此锁的线程数
  • 尝试以原子方式将内存位置从解锁切换到锁定。如果我们成功了,减少等待线程的数量并停止,我们就持有锁
  • 在操作系统中有条件地阻塞
  • 转至步骤5
  • 解锁

  • 以原子方式将保持锁定状态的内存位置设置为unlocked
  • 如果操作系统中等待此锁的线程数大于零,请告诉操作系统取消阻止所有等待此锁的线程
  • 请注意,操作系统必须实现某种机制,以避免在线程试图阻止任何等待在操作系统中的线程之前,请求取消阻止任何线程的竞争。方法因操作系统而异。例如,Linux有一种称为“futex”的东西,它本质上是一种实现原子锁定伪代码的步骤4、5和6的方法

    注意:如果您试图在代码中实现此算法,请理解您可能会生成一个性能不如正确实现的玩具。您需要深入了解特定于平台的知识,以避免可能陷入的糟糕性能陷阱。例如,编写一个自旋锁很容易,这样它就可以从另一个与超线程共享CPU中物理内核的线程窃取内核执行资源。而且很容易对成功的交换进行编码,以便CPU的分支预测预测它将失败,并且当您获得锁时,您将受到可怕的分支预测失误惩罚。

    解释如下:

    等待怎么样?

    现在是棘手的部分。嗯,只有在某种程度上它是棘手的,另一方面它是简单的。上述测试和设置机制不支持线程等待该值(除了CPU密集型自旋锁)。CPU并不真正理解高级线程和进程,因此无法实现等待。操作系统必须提供等待功能

    为了让CPU正确等待,调用者需要进行系统调用。它是唯一能够同步各种线程并提供等待功能的东西。因此,如果我们必须等待一个互斥体,或者释放一个等待的互斥体,我们别无选择,只能调用操作系统。大多数操作系统都内置了互斥原语。在某些情况下,它们提供成熟的互斥体。所以,如果一个系统调用确实提供了一个完整的互斥锁,为什么我们还要在用户空间中进行任何类型的测试和设置呢?答案是系统调用有相当大的开销,应该尽可能避免

    在这一点上,不同的操作系统差异很大,并且随着时间的推移可能会发生变化。在linux下,有一个系统调用futex,它提供了类似互斥的语义。它是专门设计的,因此可以在用户空间中完全解决非争用情况。争用情况随后被委托给操作系统,以安全的方式处理,尽管成本要高得多。然后,等待作为操作系统进程调度器的一部分进行处理


    视情况而定。典型的实现是在操作系统中完成的。它在旋转循环中等待一段时间,但随后线程从可运行队列中移除,并放入与互斥体关联的列表中。释放互斥体时,将从与互斥体关联的列表中删除一个线程,并在“等待”的有趣部分中标记为“可再次运行”,而“阻塞”指的是与操作系统调度程序的交互。这基本上是一个直到稍后某个时间点才被安排的请求。