首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >pthread_spinlock与boost::smart_ptr::spinlock的差异?

pthread_spinlock与boost::smart_ptr::spinlock的差异?
EN

Stack Overflow用户
提问于 2012-06-20 01:33:46
回答 1查看 2.3K关注 0票数 7

我在boost::smart_ptr中找到了以下自旋锁代码

代码语言:javascript
复制
bool try_lock()
{
    return (__sync_lock_test_and_set(&v_, 1) == 0);
}
void lock()
{    
    for (unsigned k=0; !try_lock(); ++k)
    {
        if (k<4)
            ; // spin
        else if (k < 16)
            __asm__ __volatile__("pause"); // was ("rep; nop" ::: "memory")
        else if (k < 32 || k & 1)
            sched_yield();
        else
        {
            struct timespec rqtp;
            rqtp.tv_sec  = 0;
            rqtp.tv_nsec = 100;
            nanosleep(&rqtp, 0);
        }
    }
}
void unlock()
{
     __sync_lock_release(&v_);
}

因此,如果我正确地理解了这一点,当锁被竞争时,传入的线程会指数般地后退,先是疯狂地旋转,然后停顿,然后产生剩余的时间片段,最后在休眠和屈服之间翻转。

我还找到了glibc pthread_spinlock实现,它使用程序集来执行锁。

代码语言:javascript
复制
#define LOCK_PREFIX "lock;" // using an SMP machine

int pthread_spin_lock(pthread_spinlock_t *lock)
{
    __asm__ ("\n"
       "1:\t" LOCK_PREFIX "decl %0\n\t"
       "jne 2f\n\t"
       ".subsection 1\n\t"
       ".align 16\n"
       "2:\trep; nop\n\t"
       "cmpl $0, %0\n\t"
       "jg 1b\n\t"
       "jmp 2b\n\t"
       ".previous"
       : "=m" (*lock)
       : "m" (*lock));

    return 0;
}

我承认我对装配的理解不是很好,所以我不完全理解这里发生了什么。(有人能解释一下这是在做什么吗?)

但是,我对boost spinlock和glibc pthread_spinlock进行了一些测试,当内核多于线程时,boost代码的性能优于glibc代码

另一方面,当线程多于内核时,glibc代码是更好的

为什么会这样呢?这两种自旋锁实现之间有什么区别,使它们在每个场景中的执行方式不同?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-07-13 00:57:07

问题中的pthread_spin_lock()实现是在哪里发布的?好像漏掉了几行重要的台词。

我看到的实现(它不是内联程序集--它是来自glibc/nptl/sysdeps/i386/pthread_spin_lock.S的独立程序集源文件)看起来类似,但是有两个附加的关键说明:

代码语言:javascript
复制
#include <lowlevellock.h>

    .globl  pthread_spin_lock
    .type   pthread_spin_lock,@function
    .align  16
pthread_spin_lock:
    mov 4(%esp), %eax
1:  LOCK
    decl    0(%eax)
    jne 2f
    xor %eax, %eax
    ret

    .align  16
2:  rep
    nop
    cmpl    $0, 0(%eax)
    jg  1b
    jmp 2b
    .size   pthread_spin_lock,.-pthread_spin_lock

它减少由传入的in参数指向的long,如果结果为零,则返回。

否则,结果是非零,这意味着这个线程没有获得锁。因此,它执行一个rep nop,这相当于pause指令。这是一个“特殊的”nop,它向CPU提示线程处于旋转状态,并且cpu应该以某种方式处理内存排序和/或分支排序,从而提高在这些情况下的性能(我并不假装完全理解在芯片的掩护下发生了什么--从软件的角度来看,这与普通的旧nop没有什么不同)。

pause之后,它再次检查这个值--如果它大于零,那么锁是无人认领的,所以它跳到函数的顶部,并试图再次声明锁。否则,它将再次跳转到pause

这个自旋锁和Boost版本的主要区别是,当它旋转时,它从未做过比pause更漂亮的事情--没有什么比sched_yield()nanosleep()更漂亮的了。所以这根线一直很热。我不清楚这在您注意到的两种行为中是如何发挥作用的,但是glibc代码会更贪婪--如果一个线程在锁上旋转,并且有其他线程准备运行,但没有可用的内核,那么旋转线程不会帮助等待线程获得任何cpu时间,而Boost版本最终会自动为等待关注的线程让路。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11111720

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档