【问题标题】:Pthreads - Increase maximum number of read locksPthreads - 增加最大读锁数
【发布时间】:2014-11-25 15:52:10
【问题描述】:

我有一个用 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;
}

在程序执行过程中的某个时间点,两个并发线程试图获得这个读锁将在这个函数中永久挂起。看到程序在整个执行过程中正确解锁了这个读写锁,我能做些什么来解决这个问题?有没有办法增加并发读锁的最大数量?为了使程序正常运行,我应该对此功能进行哪些更改?

【问题讨论】:

    标签: c multithreading


    【解决方案1】:

    在实现了 rwlocks 之后,我可以非常自信地说,可能没有办法增加系统上并发读取锁的最大数量,而且绝对没有可移植的方式来做到这一点。

    在某些基本级别上,rwlock 包含一些当前读锁数量的计数器,该计数器是一个简单的变量,如 int 或 short,每次读锁递增,解锁时递减。如果它是一个短的,你可以对你的操作系统提供商大喊大叫(即使持有 64k 读锁看起来很奇怪)如果它是一个 int,你的程序可能已经损坏并且不会释放读锁,因为它应该很难获得十亿或 4 个读取锁而没有某个地方的错误。

    我说的是十亿,因为实现 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) 后不会出错,所以我没有再费心去运行它。您可能不想持有那么多读锁。

    【讨论】:

    • 我将再次检查代码库,以确保我不会忘记释放锁。
    • 尝试一个简单的程序,计算在返回 EAGAIN 之前您可以获得读锁的次数。
    【解决方案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 实现仅在 __nr_readers__nr_readers_queued 溢出时返回 EAGAIN

        /* Overflow.  */
    8:  decl    NR_READERS(%rdi)
        movl    $EAGAIN, %edx
        jmp 9b
    
        /* Overflow.  */
    4:  decl    READERS_QUEUED(%rdi)
        movl    $EAGAIN, %edx
        jmp 9b
    

    【讨论】:

    • 感谢您对此事有所了解,我将重新检查代码库。
    【解决方案3】:

    确实有一部分代码没有正确释放锁,因此它被挂起。尽管如此,自从我开始使用读写锁以来,我一直有这个问题,因为文档并不清楚最大值的定义位置和方式。感谢您提供的所有出色而详细的意见。希望这个问题能成为其他程序员的警告和学习机会!

    【讨论】:

    • 如果你使用现代 C++(作用域锁),你永远不会遇到这样的问题。
    • 不知道。我的程序是用纯 C 编写的,所以我没有这种改进可供我使用。
    猜你喜欢
    • 2011-01-25
    • 2014-12-01
    • 2020-05-19
    • 2014-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多