为什么在写锁定挂起时可以获取读锁?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么在写锁定挂起时可以获取读锁?相关的知识,希望对你有一定的参考价值。

根据pthread_rwlock_rdlock的POSIX文档“如果编写器没有锁定并且锁上没有写入器阻塞,则调用线程获取读锁定。”我似乎发现即使编写器被阻止也可以获取读锁定。我写的一个小样本的输出如下所示:

first reader acquiring lock...
first reader lock acquired
first writer acquiring lock...
second reader acquiring lock...
second reader lock acquired
first reader releasing lock
second reader releasing lock
first writer lock acquired
first writer releasing lock

有关我的代码有什么问题,或者我没有正确理解的问题的任何建议?顺便说说:

$ make
gcc -g -I. -I../../isilib -c -Wpointer-arith -Wall -pedantic-errors
-D_POSIX_C_SOURCE=200809L -std=c99 -g rwlock_test1.c -o rwlock_test1.o
gcc rwlock_test1.o -L../../isilib -lisi -lrt -lm -pthread -o rwlock_test1
$ uname -a
Linux BLACKHEART 3.5.0-17-generic #28-Ubuntu SMP Tue Oct 9 19:31:23 UTC 
2012 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#define PTH_create( a, b, c, d ) 
    (pthread_create( (a), (b), (c), (d) ) != 0 ? abort() : (void)0 )

#define PTH_join( a, b ) 
    (pthread_join( (a), (b) ) != 0 ? abort() : (void)0 )

#define PTH_rwlock_rdlock( a ) 
    (pthread_rwlock_rdlock( (a) ) != 0 ? abort() : (void)0 )

#define PTH_rwlock_wrlock( a ) 
    (pthread_rwlock_wrlock( (a) ) != 0 ? abort() : (void)0 )

#define PTH_rwlock_unlock( a ) 
    (pthread_rwlock_unlock( (a) ) != 0 ? abort() : (void)0 )

static void *firstReader(
    void *arg
);

static void *firstWriter(
    void *arg
);

static void *secondReader(
    void *arg
);

static pthread_rwlock_t rwlock  = PTHREAD_RWLOCK_INITIALIZER;

int main( int argc, char **argv )
{
    pthread_t   thr1;
    pthread_t   thr2;
    pthread_t   thr3;

    PTH_create( &thr1, NULL, firstReader, NULL );
    PTH_create( &thr2, NULL, firstWriter, NULL );
    PTH_create( &thr3, NULL, secondReader, NULL );

    PTH_join( thr1, NULL );
    PTH_join( thr2, NULL );
    PTH_join( thr3, NULL );

    return 0;
}

static void *firstReader( void *arg )
{
    printf( "first reader acquiring lock... 
" );
    PTH_rwlock_rdlock( &rwlock );
    printf( "first reader lock acquired 
" );
    sleep( 10 );
    printf( "first reader releasing lock 
" );
    PTH_rwlock_unlock( &rwlock );
    return NULL;
}

static void *firstWriter( void *arg )
{
    sleep( 2 );
    printf( "first writer acquiring lock... 
" );
    PTH_rwlock_wrlock( &rwlock );
    printf( "first writer lock acquired 
" );
    sleep( 10 );
    printf( "first writer releasing lock 
" );
    PTH_rwlock_unlock( &rwlock );
    return NULL;
}

static void *secondReader( void *arg )
{
    sleep( 5 );
    printf( "second reader acquiring lock... 
" );
    PTH_rwlock_rdlock( &rwlock );
    printf( "second reader lock acquired 
" );
    sleep( 5 );
    printf( "second reader releasing lock 
" );
    PTH_rwlock_unlock( &rwlock );
    return NULL;
}

附加信息:

从posix标准:宏_POSIX_THREAD_PRIORITY_SCHEDULING指示是否支持线程执行调度选项。来自unistd.h:“如果定义了这些符号,相应的特征总是可用的......”然后列出_POSIX_THREAD_PRIORITY_SCHEDULING。再次来自posix:“如果支持线程执行调度选项,并且锁中涉及的线程正在使用调度策略SCHED_FIFOSCHED_RR执行,则如果编写者持有锁,调用线程将不会获取锁定。”所以我有一个程序(下面)在我的Linux系统上显示_POSIX_THREAD_PRIORITY_SCHEDULING被定义,但是我无法强制线程策略到SCHED_RR(我也尝试过SCHED_FIFO,bt没有在程序中显示)。

额外的想法?谢谢大家...

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#define PTH_create( a, b, c, d ) 
    (pthread_create( (a), (b), (c), (d) ) != 0 ? abort() : (void)0 )

#define PTH_join( a, b ) 
    (pthread_join( (a), (b) ) != 0 ? abort() : (void)0 )

static void *driver(
    void *arg
);

int main( int argc, char **argv )
{
    pthread_attr_t  attr;
    pthread_attr_init( &attr );
    pthread_attr_setschedpolicy( &attr, SCHED_RR );

    pthread_t   thrID;

    PTH_create( &thrID, &attr, driver, NULL );
    printf( "%ld
", _POSIX_THREAD_PRIORITY_SCHEDULING );
    struct sched_param  param;
    int                 policy;
    pthread_getschedparam( thrID, &policy, &param );
    if ( policy == SCHED_FIFO )
        puts( "SCHED_FIFO" );
    else if ( policy == SCHED_RR )
        puts( "SCHED_RR" );
    else if ( policy == SCHED_FIFO )
        puts( "SCHED_FIFO" );
    else if ( policy == SCHED_OTHER )
        puts( "SCHED_OTHER" );
    else
        puts( "eh?" );

    PTH_join( thrID, NULL );

    return 0;
}

static void *driver( void *arg )
{
    sleep( 2 );
    return NULL;
}

$ ./sched_test
200809
SCHED_OTHER
答案

你在POSIX错过了这句话:

如果不支持“线程执行调度”选项,则执行定义是否在调度程序未保持锁定且锁定上存在写入程序时调用线程获取锁定。

你不能依赖POSIX rwlocks而不是读者。

另一答案

看起来像Linux pthreads实现中的一个错误。在FreeBSD上正常工作:

first reader acquiring lock... 
first reader lock acquired 
first writer acquiring lock... 
second reader acquiring lock... 
first reader releasing lock 
first writer lock acquired 
first writer releasing lock 
second reader lock acquired 
second reader releasing lock 
另一答案

简单的说:

  • 任何数量的读者都可以同时持有锁。
  • 一次最多只有一个读卡器可以锁定。
  • 读者和作者不能同时持有锁。

举个例子:

  • first reader acquiring lock...→好的,因为没人拿着锁
  • first reader lock acquired
  • first writer acquiring lock...→阻止因为读者持有锁
  • second reader acquiring lock...→好的,因为只有读者持有锁
  • second reader lock acquired
  • first reader releasing lock→第二位读者仍然持有锁
  • second reader releasing lock→没有人再拿着锁了
  • first writer lock acquired
  • first writer releasing lock

此外,没有通用的解决方案:第二个读取器可能会在第一个读取器之前释放锁定,在这种情况下,上述behvaior将是有益的。或者第二个读者可能会运行很长时间,而第一个读者和作者很快就完成了,在这种情况下,严格按照请求的顺序授予资源是有益的。

以上是关于为什么在写锁定挂起时可以获取读锁?的主要内容,如果未能解决你的问题,请参考以下文章

从源代码构建奇偶校验后,货物构建与“阻止等待注册表索引上的文件锁定”挂起

应用退出或挂起时处理推送负载

javascript 文件挂起时 SignalR 连接块

线程竞争和锁定关键字

NSURLSessionDownloadTask 在挂起时继续下载

挂起时GmsClient“在仍然连接时调用connect()”