旋转锁实现(OSSpinLock)

我刚开始研究multithreading编程和线程安全性。 我熟悉忙碌的等待,经过一些研究,我现在熟悉自旋锁背后的理论,所以我想我会看一下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等效的)线程切换调用。

我实际上并不认为他们是那么不同。 在第一种情况下:

 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。 事实上,第二个例子似乎只是将这部分留给程序员[插入你在这里继续代码的首选方式],所以从某种意义上讲,它不像第一个那样完整的实现。

我对整个事情的看法,我正在评论第一个片段,就是他们试图在能够快速获得锁定(1000次迭代)和不过多地占用CPU之间取得平衡(因此我们如果锁不可用,最终会切换。

您可以通过多种不同方式实现自旋锁。 如果您找到另一个针对Windows的SpinLock实现,您将看到另一种算法(它可能涉及SetThreadPrioritySleepSwitchToThread )。

ARRSpinLockLock默认实现非常聪明,在第一个旋转周期之后它会“ ARRSpinLockLock ”一段时间的线程优先级,这具有以下优点:

  • 它为拥有锁的线程提供了更多机会释放它;
  • 它浪费了较少的CPU时间 (和电源 !)执行NOPPAUSE

Windows实现不会这样做,因为Windows API不提供这样的机会(没有等效的thread_switch()函数,并且对SetThreadPriority多次调用可能效率较低)。