2

我正在考虑使用以下机制来同步两个/多个线程。我认为唯一的缺点是 CPU 使用率。请分享您对此机制的意见。这个实现有什么问题吗?(假设 gcc 的 _sync *函数是可移植的)

//logic is that if lock = 1 means a thread has acquired the lock.
//                 lock = 0 means lock is free.
// acquireLock:
//        add 1 to lock if value is 1 this thread gets the lock
//        if value is > 1 mean some one else have taken the lock, so decrement the count


int lock = 0 ; // global variable.


void acquireLock()
{

    while(__sync_add_and_fetch (&lock,1) > 1)
    {
        __sync_add_and_fetch (&lock,-1);
    }

}

void releaseLock()
{
     __sync_add_and_fetch (&lock,-1);
}

因此,任何想要访问共享或全局数据的线程都将首先调用acquireLock,访问全局数据,然后再releaseLock。

4

2 回答 2

5

您正在实现一种自旋锁。您可以使用 CAS 循环代替递增和递减:

void acquireLock () {
    while (!_sync_bool_compare_and_swap(&lock, 0, 1)) {}
}

void releaseLock () {
    int unlock_success = _sync_bool_compare_and_swap(&lock, 1, 0);
    assert(unlock_success);
}

比较和交换将检查变量内容是否与预期值匹配(第二个参数)。如果是,它会将变量设置为新值(第三个参数)。如果预期值不匹配,则调用失败。这是一个原子操作。

于 2013-06-28T01:29:57.453 回答
1

@jxh建议的是一个很好的解决方案。但是,由于您还询问您的解决方案可能有什么问题,所以我的看法是:

您将“锁定获取失败”分为两个步骤:

  1. 尝试获取锁(增加标志)
  2. 如果失败则备份(减少标志)

所以即使一个线程未能获得锁,它仍然会修改全局状态(增量),只是为了稍后修复这个修改,在另一个时间。问题是不能保证“修复”会立即、很快或根本不会发生。递增和递减本身是原子的,但不是一对。

基本上,如果某个线程尝试获取锁但失败了,它可能无法立即递减计数器(可以换出线程)。现在,即使当前锁持有者释放了锁,其他线程也无法再获取锁,因为该值将始终大于 1(即,直到“休眠者”重新启动并减量)。这要么会造成瓶颈(所有线程都依赖于所有“失败”的线程来进行减量),要么会造成死锁(如果“失败”的线程因任何原因而增加并死亡)。

@jxh 的解决方案不会发生这种情况,因为锁获取(无论是失败还是成功)作为一个整体是一个原子步骤。

于 2013-11-06T20:50:26.047 回答