我正在考虑使用以下机制来同步两个/多个线程。我认为唯一的缺点是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,访问全局数据,然后释放Lock。
答案 0 :(得分: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);
}
比较和交换将检查变量内容是否与期望值(第二个参数)匹配。如果是,则将变量设置为新值(第三个参数)。如果预期值不匹配,则调用失败。这是一个原子操作。
答案 1 :(得分:1)
@jxh suggested是一个很好的解决方案。但是,既然你也问过你的解决方案可能出了什么问题,那么这就是我的看法:
您将“锁定获取失败”分为两个步骤:
因此,即使线程无法获取锁定,它仍然会修改全局状态(增量),以便稍后在另一时间修复此修改。问题在于无法保证“修复”会立即发生,很快发生,或者根本不会发生。增量和减量本身就是原子的,但不是一对。
基本上,如果某个线程试图获取锁并失败,则可能无法立即递减计数器(线程可以换出)。现在,即使当前锁定持有者释放锁定,其他线程也不能再获取锁定,因为该值将始终大于1(即,直到“睡眠者”重新开启并执行减量)。这会产生瓶颈(所有线程都依赖于所有“失败”线程来执行它们的减量)或死锁(如果“失败”线程增加并因任何原因而死)。
@ jxh的解决方案不会发生这种情况,因为锁定获取(无论是失败还是成功)都是一个原子步骤。