【问题标题】:spin locks not consuming 100% cpu自旋锁不消耗 100% cpu
【发布时间】:2020-04-28 06:58:41
【问题描述】:
#include "boost/smart_ptr/detail/spinlock.hpp"
boost::detail::spinlock lock;
main(){
    std::lock_guard<boost::detail::spinlock> guard(lock);
    while(true)
        {
                i=i+100;
        }
}

机器详情:

CPU:2

在线 CPU 列表:0,1

每个内核的线程数:1

每个插槽的核心数:2

套接字:1

上面的代码,当我跑的时候:

First instance =&gt; 占用了 100% 的 cpu(根据 top 命令)

Second instance =&gt; 花费了 97-98%,这两个实例的总和约为 195%-197%

我的假设是,一旦自旋锁获得 cpu,它就不会被 cpu 抢占(它不会被 cpu 通过调度其他线程来切换,此时保持线程(自旋锁定)处于调度状态队列),因此我期待第三个实例失败。 但它运行显示前两个进程线程被抢占了。

我哪里弄错了?

【问题讨论】:

  • 请显示minimal reproducible example 以及您正在使用的实际代码。您的示例中的锁没有争用,因此您的代码将直接传递到 while 循环
  • @AlanBirtles 这是我用来测试我的理解的相同代码。结果来自同一段代码。是的,没有争用,但是 spin_lock 的实现不会确保线程的上下文切换根本不会发生,而不管争用如何?争用在这里有什么意义?
  • 如果只有一个线程锁定锁,您不妨删除锁。您只是在测量 while 循环的性能
  • @AlanBirtles:对。它会旋转(等待),而不是离开线程,只有当其他线程获得了 CPU 时。所以,理想情况下,我应该测试三个线程在锁上旋转
  • 我想你误解了自旋锁是什么。它是一个线程同步原语,不断测试其值以检查它是否已解锁。一旦它被锁定,它不会做任何事情,直到它被解锁。如果没有自旋锁,您的代码的行为将完全相同

标签: c++ multithreading locking spinlock top-command


【解决方案1】:

我认为您误解了自旋锁是什么。没有比这复杂多少:

class SpinLock {
public:
    void lock() {
        while (is_locked) { /*do nothing*/ }
        // ...MAGIC HAPPENS HERE...
        is_locked = true;
    }

    void unlock() {
        is_locked = false;
        // ...SUBTLE magic happens here...
    }

private:
    bool is_locked = false;
};

MAGIC 是使用特殊机器指令* 的代码,以确保如果多个线程同时在 while 循环中“旋转”,则只有其中 一个当其他线程调用unlock() 函数时,将看到is_locked == false 并退出循环。

我的假设是,一旦自旋锁获得 cpu...

自旋锁中没有任何东西可以“获取”CPU。这只是得到的代码 by 一个 CPU 运行,与您程序中的任何其他代码没有什么不同。经营的 系统 (OS) 决定在哪个 CPU 上运行哪个线程以及何时运行,什么都不做 自旋锁确实会影响这一点。

...它不会被 CPU 抢占。

CPU 不会抢占任何东西。 CPU 只是执行代码。当 CPU 发生时 要运行操作系统代码,操作系统可以选择抢占当前线程。旋转 锁对于哪个线程被抢占、何时被抢占或为什么被抢占没有任何影响。

“抢占”意味着操作系统暂停一些正在运行的线程并允许其他一些 线程轮流运行。它可以发生大约 100 次 其次,通常情况下,所涉及的线程都没有意识到这一点。

自旋锁对抢占没有影响的原因是,它们只是 代码。纯自旋锁不会调用操作系统或与操作系统通信 反正。操作系统无法分辨线程之间的区别 计算 pi 的位数,或者平衡银行账户的线程,或者等待自旋锁的线程。


unlock() 函数中的SUBTLE magicmemory barrier instructions 组成,用于 强制执行 C++ memory model。这是一个深奥的话题—— 这个答案太深了。


* C++ Atomic operations library 为您提供对那些“特殊”机器指令的低级访问权限。

【讨论】:

  • 感谢您的回答。假设有4个线程,其中3个在while循环中旋转,第4个即将调用unlock。在您说“这里发生微妙的魔法”的 unlock() 内部,必须是负责使 is_locked==false 仅对一个线程可见的地方(out o 3 spin in while loop),这就是一个线程如何破坏while 循环。为什么再次需要其他“魔法在这里发生……”?其次,也不应该将 is_locked 设置为 true 作为 while 循环结束后的第一件事?
  • unlock() 调用中的屏障只需要确保可能在不同 CPU 上运行的其他线程将看到 is_locked 的更改值。这是lock() 函数中的魔力,它确保只有那些其他线程线程中的一个线程可以“赢得”锁定。在许多指令集架构 (ISA) 上,将使用 compare and swap instruction_ 的某些变体。在一些 ISA 上,使用了一对指令 load linked and store conditional
猜你喜欢
  • 2012-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-25
  • 2016-10-27
  • 2021-11-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多