[英]The efficiency of using a pthread_rwlock when there are a lot of readers
[英]Prioritization of pthread_rwlock?
考慮以下示例代碼:
#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的手冊頁,因為保持有相同的優先級和所有線程的調度策略作家被設置為SCHED_RR(2)閱讀器2 不應獲取鎖。
如果支持“線程執行調度”選項,並且鎖中涉及的線程正在使用調度策略SCHED_FIFO或SCHED_RR執行,則如果寫者持有該鎖或阻塞了更高或同等優先級的寫者,則調用線程將不會獲取該鎖。在鎖上 否則,調用線程將獲得鎖。
僅當兩個閱讀器都釋放了rw鎖時,編寫者才獲得該鎖。
仔細閱讀手冊。
請注意,您引用的句子
如果寫入者未持有該寫入者並且鎖上沒有任何寫入者,則調用線程將獲得讀取鎖定。
不說,如果有 (阻塞作家的讀者不會獲取鎖if
不是if and only if
。
以下將使用POSIX文檔。 帶引號的句子之后的段落指定了如果鎖中有寫程序被阻塞的情況, pthread_rwlock_rdlock()
行為:
[TPS] [選項開始]如果支持“線程執行調度”選項,並且鎖中涉及的線程正在使用調度策略SCHED_FIFO或SCHED_RR執行,則如果寫者持有該鎖或寫者,則調用線程將不獲取該鎖。具有更高或相等優先級的鎖被鎖定; 否則,調用線程將獲得鎖。 [選項結束]
[TPS TSP] [Option Start]如果支持Threads Execution Scheduling選項,並且鎖中涉及的線程正在使用SCHED_SPORADIC調度策略執行,則如果寫入者持有該鎖或如果寫入者持有該鎖,則調用線程將不會獲得該鎖。較高或相等優先級被鎖定在鎖上; 否則,調用線程將獲得鎖。 [選項結束]
如果不支持“線程執行調度”選項,則由實現方式定義,當寫程序不持有該鎖且有寫程序被阻塞時,調用線程是否獲取該鎖。 如果寫者持有該鎖,則調用線程將不獲取讀鎖。 如果未獲取讀鎖,則調用線程將阻塞,直到它可以獲取鎖為止。 如果在調用時它擁有寫鎖,則調用線程可能會死鎖。
因此,要提供完整的答案,將要求您發布實現是否提供了“線程執行調度”選項,如果提供,則選擇了哪種調度策略。
要查看當前的調度策略是什么(如果您使用的是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,否則引用的文檔的前兩段均不適用。 在這種情況下,調度行為是實現定義的。 特別是,讀取器/寫入器鎖很容易傾向於使用讀取器 ,在這種情況下,寫入器幾乎肯定不會為您的程序運行,因為讀取器正在對保護stdout
的鎖進行序列化(通過printf()
),根據C11草案標准n1570:
7.21輸入/輸出
7.21.2流
7每個流都有一個關聯的鎖,該鎖用於防止多個執行線程訪問一個流時發生數據爭用,並限制由多個線程執行的流操作的交織。 一次只能有一個線程持有此鎖。 該鎖是可重入的:單個線程可以在給定時間多次持有該鎖。
由於此鎖定是在保持readlock的同時保持的,並且sleep()
在保持readlock的同時也會執行,並且讀取器在釋放readlock和再次獲取它之間不執行任何操作,因此在任何時候都沒有讀取器持有readlock鎖很小。 因此,作家永遠沒有機會。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.