我发现通过Internet上的许多读写旋转锁实现非常复杂。我在C 中写了一个简单的读写锁。
如果我缺少任何东西,有人可以告诉我吗?
int r = 0;
int w = 0;
read_lock(void)
{
atomic_inc(r); //increment value atomically
while( w != 0);
}
read_unlock(void)
{
atomic_dec(r); // Decrement value atomically
}
write_lock(void)
{
while( (r != 0) &&
( w != 0))
atomic_inc(w); //increment value atomically
}
write_unlock(void)
{
atomic_dec(w); //Decrement value atomically
}
用法将如下。
read_lock()
// Critical Section
read_unlock();
write_lock()
// Critical Section
write_unlock();
编辑:
感谢您的答案。我现在将答案更改为原子等效
如果线程访问r
和w
同时具有数据率。如果C 程序具有数据率,则该程序的行为是未定义的。
int
不能由C 标准保证为原子。即使我们假设访问int
的系统是原子,即使在此类系统上,operator++
也可能不是原子操作。因此,同时增量可能会"消失"。
此外,在write_lock
中的循环之后,另一个线程也可以在w
递增之前结束循环,从而允许多个同时的作家 - 我认为此锁应该可以预防。
最后,这似乎是实现自旋锁的尝试。Spinlock具有优势和缺点。他们的缺点是,他们在阻塞时消耗了所有线程的CPU周期。这是对资源的高效利用,并且对电池时间有害,并且对于可能使用这些周期的其他过程不利。但是,如果等待时间很短,则可以是最佳的。
最简单的实现是使用单个积分值。-1显示当前的写入状态,0表示它没有被读取或写入,正值表明它正在被许多线程读取。
使用atomic_int和compare_exchange_weak(或强大但弱的就足够)
std::atomic_int l=0;
void write_lock() {
int v = 0;
while( !l.compare_exchange_weak( v, -1 ) )
v = 0; // it will set it to what it currently held
}
void write_unlock() {
l = 0; // no need to compare_exchange
}
void read_lock() {
int v = l.load();
while( v < 0 || !l.compare_exchange_weak(v, v+1) )
v = l.load();
}
void read_unlock() {
--l; // no need to do anything else
}
我认为这应该有效,并且具有RAII对象,即创建一个自动对象,可以锁定构造并解锁每种类型的破坏。
可以这样做的:
class AtomicWriteSpinScopedLock
{
private:
atomic_int& l_;
public:
// handle copy/assign/move issues
explicit AtomicWriteSpinScopedLock( atomic_int& l ) :
l_(l)
{
int v = 0;
while( !l.compare_exchange_weak( v, -1 ) )
v = 0; // it will set it to what it currently held
}
~AtomicWriteSpinScopedLock()
{
l_ = 0;
}
};
class AtomicReadSpinScopedLock
{
private:
atomic_int& l_;
public:
// handle copy/assign/move issues
explicit AtomicReadSpinScopedLock( atomic_int& l ) :
l_(l)
{
int v = l.load();
while( v < 0 || !l.compare_exchange_weak(v, v+1) )
v = l.load(); }
}
~AtomicReadSpinScopedLock()
{
--l_;
}
};
锁定要写入值必须为0,并且必须将其交换为-1,因此请继续尝试这样做。
锁定要读取该值必须是非负的,然后您尝试增加它,因此可以对其他读者进行重试,而不是在获取锁时,而是在设置其计数时进行。
Compare_exchange_weak设置为第一个参数,如果交易所失败,则实际保存的内容,第二个参数是您尝试将其更改为的参数。如果互换,它将返回为true,如果没有交换,则返回。
有多高效?这是一个旋转锁。它将在等待时使用CPU周期,因此最好很快可以使用:更新或读取数据应迅速进行。