我有一个用pthreads实现的多线程C程序,它使用读写锁来保护特定的数据结构。 pthread_rwlock_rdlock
,它应该是一个阻塞调用,可能会失败并在调用时返回值EAGAIN。文档说:
如果出现以下情况,pthread_rwlock_rdlock()和pthread_rwlock_tryrdlock()函数可能会失败:
[EAGAIN]
无法获取读锁定,因为已超出rwlock的最大读锁定数。
这意味着在任何给定时间点都有最大数量的线程可以获得读锁定。考虑到这一点,我创建了一个函数,它检查返回值并无休止地循环,直到它实际获得读锁定。
void
cache_rdlock(void)
{
int result= pthread_rwlock_rdlock(&cache_access);
if(result== EAGAIN)
{
while((result= pthread_rwlock_rdlock(&cache_access))== EAGAIN);
}
return;
}
在程序执行期间的某个时刻,尝试获取此读锁定的两个并发线程将永久挂起在此函数中。看到程序在整个执行过程中正确解锁了这个读写锁,我该怎么做才能解决这个问题?有没有办法增加并发读锁的最大数量?我应该对此函数进行哪些更改才能使程序正常工作?
答案 0 :(得分:3)
实现了rwlock后,我可以非常自信地说,可能没有办法增加系统上的最大并发读锁数,绝对没有可行的方法。
在某个基本级别,rwlock包含一些当前读锁定数量的计数器,该计数器是一个简单的变量,如int或short,对于每个读锁定递增,在解锁时递减。如果它是一个简短的,你可以大吼大叫你的操作系统提供商使其更大(即使看起来很奇怪,持有64k读锁)如果它是一个int,你的程序可能会被破坏并且不会释放读锁,因为它应该很难在没有错误的地方获得十亿或四个读锁。
我说十亿,因为一个非常流行的实现rwlocks的方法是使用一个32位int,它使用两个最低位来指定写锁定。
这是一个可以使用的简单测试程序:
#include <pthread.h>
#include <stdio.h>
#include <limits.h>
int
main(int argc, char **argv)
{
unsigned long long i;
pthread_rwlock_t rw;
int r;
pthread_rwlock_init(&rw, NULL);
for (i = 0; i < INT_MAX; i++) {
if ((r = pthread_rwlock_rdlock(&rw)) != 0)
break;
if (i % 10000000 == 0)
printf("%llu\n", i);
}
printf("%d %llu\n", r, i);
return 0;
}
MacOS突破1600万(2 ^ 24),Linux在20亿(2 ^ 31)之后没有出错,所以我没有打扰它进一步运行。你可能不想持有那么多读锁。
答案 1 :(得分:2)
您可能忘记释放读锁定。
因为给定pthread_rwlock_t
定义(x86_84):
typedef union
{
struct
{
int __lock;
unsigned int __nr_readers;
unsigned int __readers_wakeup;
unsigned int __writer_wakeup;
unsigned int __nr_readers_queued;
unsigned int __nr_writers_queued;
int __writer;
int __shared;
unsigned long int __pad1;
unsigned long int __pad2;
/* FLAGS must stay at this position in the structure to maintain
binary compatibility. */
unsigned int __flags;
# define __PTHREAD_RWLOCK_INT_FLAGS_SHARED 1
} __data;
char __size[__SIZEOF_PTHREAD_RWLOCK_T];
long int __align;
} pthread_rwlock_t;
如果您忘记释放锁定,则最有可能发生unsigned int __nr_readers
的溢出。
pthread_rwlock_rdlock
的x86-64实施仅在EAGAIN
和__nr_readers
溢出时返回__nr_readers_queued
:
/* Overflow. */
8: decl NR_READERS(%rdi)
movl $EAGAIN, %edx
jmp 9b
/* Overflow. */
4: decl READERS_QUEUED(%rdi)
movl $EAGAIN, %edx
jmp 9b
答案 2 :(得分:0)
确实有一部分代码没有正确释放锁定,因此它悬挂的原因。尽管如此,自从我开始使用读写锁以来,我一直都有这个问题,因为文档并不清楚最大值的定义位置和方式。感谢您提供的所有精彩和详细的输入。希望这个问题可以作为警告,也可以作为其他程序员的学习机会!