1

我刚刚开始研究多线程编程和线程安全。我熟悉忙等待,经过一些研究,我现在熟悉自旋锁背后的理论,所以我想我会看看 OSSpinLock 在 Mac 上的实现。它归结为以下函数(在 objc-os.h 中定义):

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

这里完全实现

在进行了一些挖掘之后,我现在对thread_switch参数的作用有了大致的了解(这个站点是我找到它的地方)。我对我所读内容的解释是,对 thread_switch 的特定调用将切换到下一个可用线程,并将当前线程的优先级降低到 1 个周期的绝对最小值。“最终”(在 CPU 时间内)该线程将再次激活并立即执行重新goto again;开始忙碌等待的指令。

不过,我的问题是,为什么这个电话实际上是必要的?我在这里找到了另一种自旋锁的实现(这次是针对 Windows),它根本不包括(Windows 等效的)线程切换调用。

4

2 回答 2

3

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).

Default implementation for ARRSpinLockLock is clever enough and after one first spinning cycle it "depress" thread priority for a while, this has following advantages:

  • 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.

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).

于 2012-10-18T07:27:44.310 回答
1

我真的不认为他们有什么不同。在第一种情况下:

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;
}

我们尝试获取锁。如果失败了,我们在for循环中旋转,如果它同时可用,我们立即尝试重新获取它,否则我们放弃 CPU。

在另一种情况下:

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);
}

注意下面的注释if,它实际上说如果操作系统给了我们这个选项,我们可以旋转或放弃 CPU。事实上,第二个示例似乎只是将这部分留给程序员[在此处插入您喜欢的继续代码的方式],因此从某种意义上说,它不像第一个示例那样是完整的实现。

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).

于 2012-10-18T07:23:32.903 回答