简体   繁体   English

旋转锁实现(OSSpinLock)

[英]Spin Lock Implementations (OSSpinLock)

I am just starting to look into multi-threaded programming and thread safety. 我刚刚开始研究多线程编程和线程安全性。 I am familiar with busy-waiting and after a bit of research I am now familiar with the theory behind spin locks, so I thought I would have a look at OSSpinLock's implementation on the Mac. 我熟悉忙碌等待,经过一些研究后,我现在熟悉自旋锁背后的理论,所以我想我会看一下OSSpinLock在Mac上的实现。 It boils down to the following function (defined in objc-os.h): 它归结为以下函数(在objc-os.h中定义):

static inline void ARRSpinLockLock(ARRSpinLock *l)
{
again:
   /* ... Busy-waiting ... */
    thread_switch(THREAD_NULL, SWITCH_OPTION_DEPRESS, 1);
    goto again;
}

( Full implementation here ) 完全实施

After doing a bit of digging, I now have an approximate idea of what thread_switch 's parameters do ( this site is where I found it). 在做了一些挖掘之后,我现在已经大致了解了thread_switch的参数是什么( 这个站点是我发现它的地方)。 My interpretation of what I have read is that this particular call to thread_switch will switch to the next available thread, and decrease the current thread's priority to an absolute minimum for 1 cycle. 我对我所读到的内容的解释是,对thread_switch的这一特定调用将切换到下一个可用线程,并将当前线程的优先级降低到1个周期的绝对最小值。 'Eventually' (in CPU time) this thread will become active again and immediately execute the goto again; '最终'(在CPU时间内)此线程将再次变为活动状态并立即再次执行goto again; instruction which starts the busy-waiting all over again. 重新开始忙碌等待的指令。

My question though, is why is this call actually necessary? 我的问题是,为什么这个电话实际上是必要的? I found another implementation of a spin-lock (for Windows this time) here and it doesn't include a (Windows-equivalent) thread switching call at all. 我发现了一个自旋锁的另一种实现方式(适用于Windows这段时间), 在这里 ,它不包括(Windows的当量)的线程切换调用的。

You can implement a spin lock in many different ways. 您可以通过多种不同方式实现自旋锁。 If you find another SpinLock implementation for Windows you'll see another algorithm for that (it may involves SetThreadPriority , Sleep or SwitchToThread ). 如果您找到另一个针对Windows的SpinLock实现,您将看到另一种算法(它可能涉及SetThreadPrioritySleepSwitchToThread )。

Default implementation for ARRSpinLockLock is clever enough and after one first spinning cycle it "depress" thread priority for a while, this has following advantages: ARRSpinLockLock默认实现非常聪明,在第一个旋转周期后,它“ ARRSpinLockLock ”一段时间的线程优先级,这具有以下优点:

  • it gives more opportunities to the thread that owns the lock to release it; 它为拥有锁的线程提供了更多机会释放它;
  • it wastes less CPU time (and power !) performing NOP or PAUSE . 它浪费了更少的CPU时间 (和功率 !)执行NOPPAUSE

Windows implementation doesn't do it because Windows API doesn't offer that opportunity (there is no equivalent thread_switch() function and multiple calls to SetThreadPriority could be less efficient). Windows实现不会这样做,因为Windows API不提供该机会(没有等效的thread_switch()函数,并且对SetThreadPriority多次调用可能效率较低)。

I actually don't think they're that different. 我实际上并不认为他们是那么不同。 In the first case: 在第一种情况下:

static inline void ARRSpinLockLock(ARRSpinLock *l)
{
    unsigned y;
again:
    if (__builtin_expect(__sync_lock_test_and_set(l, 1), 0) == 0) {
        return;
    }
    for (y = 1000; y; y--) {
#if defined(__i386__) || defined(__x86_64__)
        asm("pause");
#endif
        if (*l == 0) goto again;
    }
    thread_switch(THREAD_NULL, SWITCH_OPTION_DEPRESS, 1);
    goto again;
}

We try to acquire the lock. 我们试图获得锁定。 If that fails, we spin in the for loop and if it's become available in the meantime we immediately try to reacquire it, if not we relinquish the CPU. 如果失败了,我们在for循环中旋转,如果它在此期间可用,我们会立即尝试重新获取它,如果不是,我们放弃CPU。

In the other case: 在另一种情况下:

inline void Enter(void)
{
    int prev_s;
    do
    {
        prev_s = TestAndSet(&m_s, 0);
        if (m_s == 0 && prev_s == 1)
        {
            break;
        }
        // reluinquish current timeslice (can only
        // be used when OS available and
        // we do NOT want to 'spin')
        // HWSleep(0);
    }
    while (true);
}

Note the comment below the if , which actually says that we could either spin or relinquish the CPU if the OS gives us that option. 请注意if下面的注释,它实际上表示如果操作系统为我们提供该选项,我们可以旋转或放弃CPU。 In fact the second example seems to just leave that part up to the programmer [insert your preferred way of continuing the code here], so in a sense it's not a complete implementation like the first one. 事实上,第二个例子似乎只是让那个部分留给程序员[插入你在这里继续代码的首选方式],所以从某种意义上说它不像第一个那样完整的实现。

My take on the whole thing, and I'm commenting on the first snippet, is that they're trying to achieve a balance between being able to get the lock fast (within 1000 iterations) and not hogging the CPU too much (hence we eventually switch if the lock does not become available). 我对整个事情的看法,我正在评论第一个片段,就是他们试图在能够快速获得锁定(1000次迭代)和不过多地占用CPU之间取得平衡(因此我们如果锁不可用,最终会切换。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM