c - pthread_rwlock的优先级?

标签 c multithreading pthreads read-write

考虑这个示例代码:

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

void *func1(void *);
void *func2(void *);

static pthread_rwlock_t rwLock = PTHREAD_RWLOCK_INITIALIZER;

int main() {

    pthread_t thread1;
    pthread_t thread2;

    pthread_create(&thread1, NULL, func1, NULL);
    sleep(1);
    int i;
    for (i = 0; i < 3; i++) {
        pthread_create(&thread2, NULL, func2, (void *)(i + 1));
    }

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    return 0;
}

void *func1(void *arg) {

    int j;
    for(j = 0; j < 10; j++) {

        printf("func 1: trying lock\n");
        pthread_rwlock_wrlock(&rwLock);
        printf("func 1: lock aquired, sleep 1 sec...\n");

        sleep(1);

        pthread_rwlock_unlock(&rwLock);
    }
}

void *func2(void *arg) {

    int true = 1;
    while(true) {

        pthread_rwlock_rdlock(&rwLock);

        printf("func 2: thread %i: lock aquired, sleep 1 sec... \n", (int)arg);
        sleep(1);

        pthread_rwlock_unlock(&rwLock);
    }
}

我有一个线程在 func1 中循环,其中请求写入锁定 1 秒,另外 3 个线程在 func 2 中循环,其中请求读取锁定 1 秒。

关于pthread_rwlock_rdlock手册页上写着“如果写入者没有持有锁并且没有写入者被锁定,则调用线程将获取读锁。”。从我在第 5 行的输出粘贴中,您可以看到“func 1:尝试锁定”上的写入者显然处于阻塞状态,那么为什么我的读者无论如何都会获得锁定呢?第 5 行之后,每秒打印 3 行。减少读取器线程会增加写入器获得锁的机会。

func 1: trying lock
func 1: lock aquired, sleep 1 sec...
func 1: trying lock
func 1: lock aquired, sleep 1 sec...
func 1: trying lock
func 2: thread 1: lock aquired, sleep 1 sec... 
func 2: thread 3: lock aquired, sleep 1 sec... 
func 2: thread 2: lock aquired, sleep 1 sec... 
func 2: thread 2: lock aquired, sleep 1 sec... 
func 2: thread 3: lock aquired, sleep 1 sec... 
func 2: thread 1: lock aquired, sleep 1 sec... 
func 2: thread 2: lock aquired, sleep 1 sec... 
func 2: thread 3: lock aquired, sleep 1 sec... 
func 2: thread 1: lock aquired, sleep 1 sec... 
func 2: thread 3: lock aquired, sleep 1 sec... 
func 2: thread 1: lock aquired, sleep 1 sec... 
func 2: thread 2: lock aquired, sleep 1 sec... 
func 2: thread 3: lock aquired, sleep 1 sec... 
func 2: thread 2: lock aquired, sleep 1 sec... 
func 2: thread 1: lock aquired, sleep 1 sec... 
func 2: thread 3: lock aquired, sleep 1 sec... 
func 2: thread 1: lock aquired, sleep 1 sec... 
func 2: thread 2: lock aquired, sleep 1 sec... 
...

添加了另一个示例

#define _GNU_SOURCE

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

#define SIZE 10000

void *writerFunc(void *);
void *readerFunc1(void *);
void *readerFunc2(void *);
int setSchedulePolicyTo2(void);

static pthread_rwlock_t rwLock = PTHREAD_RWLOCK_INITIALIZER;

int main() {

    pthread_t readerThread1;
    pthread_t readerThread2;
    pthread_t writerThread;

    pthread_create(&readerThread1, NULL, readerFunc1, NULL);
    sleep(1);
    pthread_create(&readerThread1, NULL, writerFunc, NULL);
    sleep(1);
    pthread_create(&readerThread2, NULL, readerFunc2, NULL);

    pthread_join(readerThread1, NULL);
    pthread_join(readerThread2, NULL);
    pthread_join(writerThread, NULL);

    return 0;
}

void *writerFunc(void *arg) {
    printf("                writer's scheduling policy: %d\n", setSchedulePolicyTo2());

    printf("writer 1: trying to acquire rw lock...(on hold)\n");
    pthread_rwlock_wrlock(&rwLock); // Note ..._wrlock
    printf("writer 1: rw lock acquired \n");
    pthread_rwlock_unlock(&rwLock);
}

void *readerFunc1(void *arg) {
    printf("                reader1's scheduling policy: %d\n", setSchedulePolicyTo2());

    printf("reader 1: trying to acquire rw lock...(on hold)\n");
    pthread_rwlock_rdlock(&rwLock);
    printf("reader 1: rw lock acquired \n");
    sleep(3); // enough time to let reader 2 to acquire rw lock before this reader releases it.
    pthread_rwlock_unlock(&rwLock);
    printf("reader 1: rw lock released \n");
}

void *readerFunc2(void *arg) {
    printf("                reader2's scheduling policy: %d\n", setSchedulePolicyTo2());

    printf("reader 2: trying to acquire rw lock...(on hold)\n");
    pthread_rwlock_rdlock(&rwLock);
    printf("reader 2: rw lock acquired \n");
    sleep(2);
    pthread_rwlock_unlock(&rwLock);
    printf("reader 2: rw lock released \n");
}

int setSchedulePolicyTo2() {
    struct sched_param sp;
        sp.sched_priority = 10;
    int policy;
    int j;
    if((j = pthread_setschedparam(pthread_self(), SCHED_RR, &sp)) != 0) {
        printf("error: %s \n", strerror(errno));
    }
    if((j = pthread_getschedparam(pthread_self(), &policy, &sp)) != 0) {
        printf("error: %s \n", strerror(errno));
    }
    return policy;
}

输出:

$ gcc main.c -pthread
$ sudo ./a.out
                reader1's scheduling policy: 2
reader 1: trying to acquire rw lock...(on hold)
reader 1: rw lock acquired 
                writer's scheduling policy: 2
writer 1: trying to acquire rw lock...(on hold)
                reader2's scheduling policy: 2
reader 2: trying to acquire rw lock...(on hold)
reader 2: rw lock acquired 
reader 1: rw lock released 
reader 2: rw lock released 
writer 1: rw lock acquired 
Segmentation fault (end of program)

根据 pthread_rwlock_rdlock 的手册页,读取器 2 不应该获取锁,因为有具有相同优先级的写入器处于搁置状态,并且所有线程的调度策略都设置为 SCHED_RR (2)。

If the Thread Execution Scheduling option is supported, and the threads involved in the lock are executing with the scheduling policies SCHED_FIFO or SCHED_RR, the calling thread shall not acquire the lock if a writer holds the lock or if writers of higher or equal priority are blocked on the lock; otherwise, the calling thread shall acquire the lock.

只有当两个读者都释放了读写锁时,写入者才会获取锁。

最佳答案

仔细阅读联机帮助页。

注意你引用的句子

The calling thread acquires the read lock if a writer does not hold the lock and there are no writers blocked on the lock

是否表示,如果有写入者被阻止,则读取者将不会获取锁(if当且仅当

以下将使用POSIX文档。 紧跟在引用的句子后面的段落指定了如果有写入者被锁阻塞时pthread_rwlock_rdlock()如何操作:

[TPS] [Option Start] If the Thread Execution Scheduling option is supported, and the threads involved in the lock are executing with the scheduling policies SCHED_FIFO or SCHED_RR, the calling thread shall not acquire the lock if a writer holds the lock or if writers of higher or equal priority are blocked on the lock; otherwise, the calling thread shall acquire the lock. [Option End]

[TPS TSP] [Option Start] If the Threads Execution Scheduling option is supported, and the threads involved in the lock are executing with the SCHED_SPORADIC scheduling policy, the calling thread shall not acquire the lock if a writer holds the lock or if writers of higher or equal priority are blocked on the lock; otherwise, the calling thread shall acquire the lock. [Option End]

If the Thread Execution Scheduling option is not supported, it is implementation-defined whether the calling thread acquires the lock when a writer does not hold the lock and there are writers blocked on the lock. If a writer holds the lock, the calling thread shall not acquire the read lock. If the read lock is not acquired, the calling thread shall block until it can acquire the lock. The calling thread may deadlock if at the time the call is made it holds a write lock.

因此,要提供完整的答案,您需要发布您的实现是否提供线程执行调度选项,如果提供,则选择哪种调度策略。

要查看当前的调度策略是什么(如果您使用的是 Linux),请运行以下程序:

#define _GNU_SOURCE
#include <stdio.h>
#include <pthread.h>

int main(void)
{
  printf("round-robin scheduling policy: %d\n", SCHED_RR);
  printf("fifo scheduling policy: %d\n", SCHED_FIFO);
  printf("other scheduling policy: %d\n", SCHED_OTHER);
  pthread_attr_t ta;
  pthread_getattr_np(pthread_self(), &ta);
  int ts;
  pthread_attr_getschedpolicy(&ta, &ts);
  printf("current scheduling policy: %d\n", ts);
}

除非当前的调度策略是 Round-Robin 或 Fifo,否则引用文档的前两段不适用。在这种情况下,调度行为是由实现定义的。特别是,读取器/写入器锁很容易优先选择读取器,在这种情况下,写入器几乎肯定永远不会为您的程序运行,因为读取器根据 C11 草案标准 n1570,在保护 stdout 的锁上进行序列化(通过 printf()):

7.21 Input/output

7.21.2 Streams

7 Each stream has an associated lock that is used to prevent data races when multiple threads of execution access a stream, and to restrict the interleaving of stream operations performed by multiple threads. Only one thread may hold this lock at a time. The lock is reentrant: a single thread may hold the lock multiple times at a given time.

由于持有该锁的同时也持有读锁,并且在持有读锁的同时执行了 sleep(),并且读取者在释放读锁和再次获取它之间不执行任何操作,因此在任何时候都没有读者持有锁的情况非常小。因此,作者永远没有机会。

关于c - pthread_rwlock的优先级?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55986628/

相关文章:

c - 使用互斥体的 Pthread 同步未正确同步单词

c - 为什么多线程比单线程慢?

c - 在 C 函数中传递参数

java - 并发收集先发生关系

c - 在c中打开文件进行读取

c# - 多个 Web 请求的最佳多线程方法

multithreading - Apache Shiro 集成和 Netty ExecutionHandler/OrderedMemoryAwareThreadPoolExecutor

取消 getchar()

c - 了解malloc的不同方法

C快速计算下一个4的倍数?