这个rwlock实现中有什么问题吗?

这个rwlock实现中有什么问题吗?,c,locking,C,Locking,我刚刚在C中实现了一个读写器锁。我想限制读写器的数量,所以我使用'num'来计算它。我不确定这个实现是否存在一些潜在的数据竞争或死锁情况。你能帮我弄清楚吗 另一个问题是,我能否以某种方式删除struct\u rBlock中的“自旋锁”?谢谢 #define MAX_READER 16; typedef _rwlock *rwlock; struct _rwlock{ spin_lock lk; unint32_t num; }; void wr_lock(rwlock

我刚刚在C中实现了一个读写器锁。我想限制读写器的数量,所以我使用'num'来计算它。我不确定这个实现是否存在一些潜在的数据竞争或死锁情况。你能帮我弄清楚吗

另一个问题是,我能否以某种方式删除struct\u rBlock中的“自旋锁”?谢谢

#define MAX_READER 16; 
typedef _rwlock *rwlock;
struct _rwlock{
    spin_lock   lk;
    unint32_t   num;
};
void wr_lock(rwlock lock){
    while (1){
        if (lock->num > 0) continue;
        lock(lock->lk);
        lock->num += MAX_READER;
        return;
    }
}
void wr_unlock(rwlock lock){
    lock->num -= MAX_READER;
    unlock(lock->lk);
}
void rd_lock(rwlock lock){
    while (1){
        if (lock->num >= MAX_READER) continue;
        atom_inc(num);
        return;
    }
}
void rd_unlock(rwlock lock){
    atom_dec(num);
}

简短回答:是的,这里有严重的问题。我不知道您使用的是什么同步库,但您没有保护对共享数据的访问,并且您将在rd_lock和wr_lock中的循环上浪费大量CPU周期。几乎在所有情况下都应该避免旋转锁,但也有例外。

简短回答:是的,这里存在严重问题。我不知道您使用的是什么同步库,但您没有保护对共享数据的访问,并且您将在rd_lock和wr_lock中的循环上浪费大量CPU周期。在几乎所有情况下都应避免旋转锁,但也有例外。

在wr_锁和类似的rd_锁中:

这是错误的。如果您没有以某种方式进行同步,就不能保证看到来自其他线程的更改。如果这是唯一的问题,您可能会获得锁,然后检查计数

在rd_锁中:

这对writer函数中的非原子+=和-=不起作用,因为它可能会中断它们。rd_解锁中的减量也是如此

rd_锁可以在线程作为编写器持有锁时返回-这不是读写器锁的常见语义,这意味着无论您的rw锁应该保护什么,它都不会保护它

如果您使用的是pthreads,那么它已经有了一个rwlock。在Windows上考虑SRWWORK从来没有用过他们自己。对于可移植代码,使用一个条件变量或两个条件变量构建rwlock——一个用于读卡器,一个用于写卡器。也就是说,只要C中的多线程代码是可移植的。C11有一个条件变量,如果有一个C11之前的线程实现没有,我不想使用它-

在wr_锁和rd_锁中类似:

这是错误的。如果您没有以某种方式进行同步,就不能保证看到来自其他线程的更改。如果这是唯一的问题,您可能会获得锁,然后检查计数

在rd_锁中:

这对writer函数中的非原子+=和-=不起作用,因为它可能会中断它们。rd_解锁中的减量也是如此

rd_锁可以在线程作为编写器持有锁时返回-这不是读写器锁的常见语义,这意味着无论您的rw锁应该保护什么,它都不会保护它


如果您使用的是pthreads,那么它已经有了一个rwlock。在Windows上考虑SRWWORK从来没有用过他们自己。对于可移植代码,使用一个条件变量或两个条件变量构建rwlock——一个用于读卡器,一个用于写卡器。也就是说,只要C中的多线程代码是可移植的。C11有一个条件变量,如果有一个C11之前的线程实现没有,我不想使用它-

谢谢大家!!我不完全理解你不保护共享数据的访问是什么意思。在这种情况下,共享数据可能仅为num。在reader的操作中,我使用原子更新操作来处理num。在writer的操作中,我认为自旋锁可以保护num。如果你的意思是“continue”语句浪费CPU时间,那么可以用“yield”来代替它。是的,我不认为使用自旋锁是个好主意。这就是我想删除它的原因:难道您没有访问pthreads之类的东西的权限吗?用它来消除旋转锁。您必须保护对数据的读写。你不保护你的阅读。我不会说收益率是自旋锁问题的解决方案,但它可以带来巨大的改进。不过我很肯定还有更好的办法。你的目标系统是什么?你是对的,我应该保护阅读。它是linux。pthread如何消除自旋锁?在pthreads中使用互斥锁?在获取锁失败的情况下,互斥锁似乎会阻塞而不是旋转循环。我在互斥、旋转锁、信号量和cv等方面有一些混乱…非常感谢。谢谢!我不完全理解你不保护共享数据的访问是什么意思。在这种情况下,共享数据可能仅为num。在reader的操作中,我使用原子更新操作来处理num。在writer的操作中,我认为自旋锁可以保护num。如果你的意思是“continue”语句浪费CPU时间,那么可以用“yield”来代替它。是的,我不认为使用自旋锁是个好主意。这就是我想删除它的原因:难道您没有访问pthreads之类的东西的权限吗?用它来消除旋转锁。您必须保护对数据的读写。你不保护你的阅读。我不会说收益率是自旋锁问题的解决方案,但它可以带来巨大的改进。还

我确信有更好的办法。你的目标系统是什么?你是对的,我应该保护阅读。它是linux。pthread如何消除自旋锁?在pthreads中使用互斥锁?在获取锁失败的情况下,互斥锁似乎会阻塞而不是旋转循环。我在互斥锁、自旋锁、信号量和cv等方面有些混乱…非常感谢。我不想在数百个线程排队获取写锁的情况下成为这台机器上的CPU。此外,在检查谓词时没有保护,只有在修改谓词时,您至少在两个地方假设了隐含的屏障。你为什么不使用pthread rwlocks?我只是想通过自己实现一个来看看rwlock是如何工作的。我提出了一个想法并实现了它。@WDan:对于几乎所有具有内置rBlock实现的系统,其工作原理的答案将不同于仅使用标准函数编写的任何系统,因为他们的rwlock实现可以在Linux中使用futex和/或直接与内核/调度器交互。他们可能会使用旋转锁,但仅适用于持有时间非常短的锁。他们在执行用户代码时不会持有自旋锁,因为另一个线程在这段时间内旋转的成本太高。令人遗憾的是,本机内核记录的RW锁直到vista/server2k8内核才引入Win32/64。是的,微软可以咬我。我不想成为那台机器上的CPU,当几百个线程排队来获取写锁的时候。此外,在检查谓词时没有保护,只有在修改谓词时,您至少在两个地方假设了隐含的屏障。你为什么不使用pthread rwlocks?我只是想通过自己实现一个来看看rwlock是如何工作的。我提出了一个想法并实现了它。@WDan:对于几乎所有具有内置rBlock实现的系统,其工作原理的答案将不同于仅使用标准函数编写的任何系统,因为他们的rwlock实现可以在Linux中使用futex和/或直接与内核/调度器交互。他们可能会使用旋转锁,但仅适用于持有时间非常短的锁。他们在执行用户代码时不会持有自旋锁,因为另一个线程在这段时间内旋转的成本太高。令人遗憾的是,本机内核记录的RW锁直到vista/server2k8内核才引入Win32/64。是的,MS会咬我。你是说当线程作为写入程序持有锁时,rd_lock可能会返回,因为我在rd_lock中读取lock->num而没有同步?我不知道rwlock的准确实现,我想知道它是如何工作的。因此,我实施了我的版本以进行测试。我以前引用过。当编写器持有锁时,rd_lock可以返回,因为rd_lock中绝对没有任何东西可以阻止这种情况发生。您可以在检查和增加读卡器计数时使用rd_lock中的自旋锁来改善问题,但这不是一个完整的解决方案。我曾经认为,当编写器持有锁时,“num”应该等于MAX_reader,因为编写器在函数wr_lock中将MAX_reader添加到num中。并且rd_锁将循环,直到写入程序完成。@WDan:写入程序检查num和写入程序增加num之间有一个间隙。在该间隙期间,任何数量的读卡器线程都可以通过。您的意思是,当线程以写入程序的身份持有锁时,rd_锁可能会返回,因为我在rd_锁中读取lock->num而没有同步?我不知道rwlock的准确实现,我想知道它是如何工作的。因此,我实施了我的版本以进行测试。我以前引用过。当编写器持有锁时,rd_lock可以返回,因为rd_lock中绝对没有任何东西可以阻止这种情况发生。您可以在检查和增加读卡器计数时使用rd_lock中的自旋锁来改善问题,但这不是一个完整的解决方案。我曾经认为,当编写器持有锁时,“num”应该等于MAX_reader,因为编写器在函数wr_lock中将MAX_reader添加到num中。rd_锁将循环,直到写入程序完成。@WDan:写入程序检查num和写入程序增加num之间有一个间隙。在这个间隙中,任何数量的读卡器线程都可以通过。
while (1){
    if (lock->num > 0) continue;
atom_inc(num);