我刚刚开始研究多线程编程和线程安全。我熟悉忙等待,经过一些研究,我现在熟悉了自旋锁背后的理论,所以我想我应该看看OSSpinLock在Mac上的实现。它归结为以下函数(在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)
在做了一些研究之后,我现在大概知道了thread_switch的参数是做什么的(this site是我找到它的地方)。我对我读到的内容的解释是,这个对thread_switch的调用将切换到下一个可用的线程,并在1个周期内将当前线程的优先级降低到绝对最小。‘最终’(在CPU时间内)这个线程将再次变得活跃,并立即执行goto again;指令,该指令再次开始忙碌等待。
我的问题是,为什么这个调用实际上是必要的?我发现了另一个自旋锁(这次是针对Windows ) here的实现,它根本不包含(与Windows等效的)线程切换调用。
发布于 2012-10-18 15:23:33
我真的不认为他们有什么不同。在第一种情况下:
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之间取得平衡(因此,如果锁不可用,我们最终会切换)。
发布于 2012-10-18 15:27:44
您可以通过许多不同的方式实现自旋锁。如果你在Windows上找到了另一个SpinLock实现,你会看到另一个算法(它可能涉及到SetThreadPriority、Sleep或SwitchToThread)。
ARRSpinLockLock的默认实现足够聪明,在第一个旋转周期后,它会在一段时间内“压低”线程优先级,这具有以下优点:
>E115><代码>E216!)执行NOP或PAUSE.
Windows实现没有做到这一点,因为Windows API没有提供这种机会(没有等效的thread_switch()函数,而且对SetThreadPriority 的多次调用可能会降低的效率)。
https://stackoverflow.com/questions/12949028
复制相似问题