Heim > Artikel > Backend-Entwicklung > Linux – Bedingungsvariable (Bedingungsvariable) implementiert das Producer-Consumer-Modell und die Lese-/Schreibsperre
1. Bedingungsvariablen
Während des Thread-Synchronisationsprozesses gibt es auch die folgenden Situationen: Thread A muss warten, bis eine bestimmte Bedingung erfüllt ist, bevor er mit der Ausführung fortfahren kann wird blockiert, während Thread B ausgeführt wird. Wenn diese Bedingung während des Prozesses auftritt, wird Thread A aktiviert, um die Ausführung fortzusetzen. Verwenden Sie Bedingungsvariablen in der Pthread-Bibliothek, um das Warten auf eine Bedingung zu blockieren oder den Thread aufzuwecken, der auf diese Bedingung wartet. Bedingungsvariablen werden durch Variablen vom Typ pthread_cond_t dargestellt.
Verwenden Sie pthread_cond_init, um die Bedingungsvariable zu initialisieren, können Sie auch die Makrodefinition PTHEAD_COND_INITIALIZER verwenden, um sie zu initialisieren, und pthread_cond_destroy verwenden, um die Bedingungsvariable zu zerstören Bei einem Fehler wird eine Fehlernummer zurückgegeben.
Eine Bedingungsvariable wird immer mit einem Mutex verwendet. Ein Thread kann pthread_cond_wait aufrufen, um eine Bedingungsvariable zu blockieren und darauf zu warten:
1. Den Mutex freigeben
3 return
Ein Thread kann pthread_cond_signal aufrufen, um einen anderen Thread aufzuwecken, der auf eine bestimmte Bedingungsvariable wartet, oder er kann pthread_cond_broadcast aufrufen, um alle Threads aufzuwecken, die auf diese Bedingungsvariable warten.
2. Verwenden Sie zur Veranschaulichung das Producer-Consumer-Modell.
Wie der Name schon sagt, müssen Sie zur Implementierung dieses Modells zunächst zwei Rollen haben (Produzent, Konsument). Natürlich muss es eine Gelegenheit geben, beide kritischen Ressourcen zugänglich zu machen (eine Gelegenheit), und die Beziehung zwischen Produzenten und Konsumenten (gegenseitiger Ausschluss) und die Beziehung zwischen Konsumenten (gegenseitiger Ausschluss) müssen verstanden werden, die Beziehung zwischen Produzenten und Konsumenten (Synchronisation und gegenseitiger Ausschluss) ist im Allgemeinen ein Ort, zwei Rollen und drei Beziehungen. Um es mit Code zu implementieren, erzeugt der Produzent ein Datenelement und sendet dann ein Signal zum Konsumieren an den Konsumenten. Nachdem der Konsument es konsumiert hat, sendet er ein Signal an den Produzenten, um den Produzenten anzuweisen, mit der Produktion fortzufahren, und so weiter.
1 #include<stdio.h> 2 #include <stdlib.h> 3 #include<malloc.h> 4 #include<pthread.h> 5 #include<semaphore.h> 6 typedef int Data_type; 7 typedef int* Data_type_p; 8 static pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;//初始化互斥锁 9 static pthread_cond_t needProduct=PTHREAD_COND_INITIALIZER;//初始化条件变量 10 11 12 typedef struct listnode //定义一个链表来存放数据(一个场所) 13 { 14 Data_type data; 15 struct listnode* next; 16 }list ,*listp,**listpp; 17 18 listp head=NULL; 19 20 static listp buyNode(Data_type _data) 21 { 22 listp tem=(listp)malloc(sizeof(list)); 23 if(tem) 24 { 25 tem -> data=_data; 26 tem -> next=NULL; 27 return tem; 28 } 29 return NULL; 30 } 31 void initList(listpp list) 32 { 33 *list=buyNode(0); 34 } 35 void push_list(listp list,Data_type _data) 36 { 37 listp cur=buyNode(_data); 38 listp tem=list; 39 while(tem->next) 40 { 41 tem=tem->next; 42 } 43 tem ->next=cur; 44 } 45 void deleteList(listp list) 46 { 47 if(list) 48 { 49 free(list); 50 list=NULL; 51 } 52 } 53 int pop_list(listp list,Data_type_p data) 54 { 55 if(list ->next==NULL) 56 { 57 *data =-1; 58 return -1; 59 } 60 listp tem=list->next; 61 list ->next=tem->next; 62 *data=tem->data; 63 deleteList(tem); 64 return 0; 65 } 66 void PrintList(listp list) 67 { 68 listp cur=list->next;; 69 while(cur) 70 { 71 printf("%d",cur->data); 72 fflush(stdout); 73 cur=cur->next; 74 } 75 printf("\n"); 76 } 77 void *product(void* arg)//定义生产者与生产者之间的关系(互斥) 78 { 79 int i=0; 80 while(1) 81 { 82 pthread_mutex_lock(&lock); 83 printf("product data:%d\n",i); 84 push_list(head,i++); 85 pthread_mutex_unlock(&lock); 86 printf("conduct is ok.weak up comsumer...\n"); 87 pthread_cond_signal(&needProduct);//当生产者有数据时,发送信号,唤醒消费者 88 sleep(2); 89 } 90 91 } 92 void *consumer(void* arg)//消费者与消费者之间的关系(互斥) 93 { 94 Data_type _data; 95 while(1) 96 { 97 pthread_mutex_lock(&lock); 98 while(-1==pop_list(head,&_data)) 99 { 100 pthread_cond_wait(&needProduct,&lock);//没收到生产者的消息之前就阻塞等待 101 } 102 printf("consumer data:%d\n",_data); 103 pthread_mutex_unlock(&lock); 104 sleep(1); 105 } 106 } 107 int main() 108 { 109 initList(&head); 110 pthread_t id1; 111 pthread_t id2; 112 pthread_create(&id1,NULL,product,NULL); 113 pthread_create(&id2,NULL,consumer,NULL); 114 pthread_join(id1,NULL); 115 pthread_join(id2,NULL); 116 return 0; 117 }Zusammenfassung: Der obige Code implementiert einen einzelnen Produzenten und einen einzelnen Konsumenten, das Produzent-Konsumenten-Modell. Einfach ausgedrückt dient er dazu, eine sich gegenseitig ausschließende Beziehung zwischen Produzenten und Konsumenten zu erreichen. und eine synchronisierte, sich gegenseitig ausschließende Beziehung zwischen Produzenten und Verbrauchern.
3. Verwenden Sie Semaphor, um das Producer-Consumer-Modell zu implementieren.
Die Mutex-Variable ist entweder 0 oder 1, was als verfügbare Menge einer Ressource angesehen werden kann. Bei der Initialisierung ist Mutex 1, was darauf hinweist, dass dies der Fall ist eine verfügbare Ressource. ,
Erhalten Sie die Ressource beim Sperren, reduzieren Sie Mutex auf 0, um anzuzeigen, dass keine weitere verfügbare Ressource vorhanden ist, geben Sie die Ressource beim Entsperren frei und erhöhen Sie Mutex wieder auf 1, um anzuzeigen, dass eine weitere verfügbare Ressource vorhanden ist. Semaphor ähnelt Mutex und stellt die Anzahl der verfügbaren Ressourcen dar. Im Gegensatz zu Mutex kann diese Zahl größer als 1 sein. Das heißt, wenn die Anzahl der durch das Semaphor beschriebenen Ressourcen 1 beträgt, sind das Semaphor und die Mutex-Sperre zu diesem Zeitpunkt gleich!
sem_init() initialisiert das Semaphor
sem_wait()P-Operation erhält Ressourcen
sem_post()V-Operation gibt Ressourcen frei
sem_destroy() zerstört das Semaphor
Das Obige ist ein in einer verknüpften Liste geschriebenes Producer-Consumer-Modell, dessen Speicherplatz dynamisch zugewiesen wird. Jetzt wird das Producer-Consumer-Modell basierend auf einer Ringwarteschlange mit fester Größe neu geschrieben
1 #include<stdio.h> 2 #include<pthread.h> 3 #include<semaphore.h> 4 #define PRODUCT_SIZE 20 5 #define CONSUMER_SIZE 0 6 7 sem_t produceSem; 8 sem_t consumerSem; 9 int Blank [PRODUCT_SIZE]; 10 11 void* product(void* arg) 12 { 13 int p=0; 14 while(1) 15 { 16 sem_wait(&produceSem); //申请资源。 17 int _product=rand()%100; 18 Blank[p]=_product; 19 printf("product is ok ,value is :%d\n",_product); 20 sem_post(&consumerSem);//释放资源 21 p=(p+1) % PRODUCT_SIZE; 22 sleep(rand()%3); 23 } 24 25 } 26 void* consumer(void* arg) 27 { 28 int p=0; 29 while(1) 30 { 31 sem_wait(&consumerSem);//申请资源 32 int _consumer=Blank[p]; 33 printf("consumer is ok,value is :%d\n",_consumer); 34 sem_post(&produceSem);//释放资源 35 p=(p+1)% PRODUCT_SIZE; 36 sleep(rand()%5); 37 } 38 } 39 int main() 40 { sem_init(&produceSem,0,PRODUCT_SIZE); 41 sem_init(&consumerSem,0,CONSUMER_SIZE); 42 pthread_t tid1,tid2; 43 pthread_create(&tid1,NULL,product,NULL); 44 pthread_create(&tid2,NULL,consumer,NULL); 45 pthread_join(tid1,NULL); 46 pthread_join(tid2,NULL); 47 sem_destroy(&produceSem); 48 sem_destroy(&consumerSem); 49 return 0; 50 }4 . Die Lese-/Schreibsperre ist eigentlich eine spezielle Spin-Sperre, die verwendet wird, um die Situation zu bewältigen, in der mehr Leser und Autoren nur auf gemeinsam genutzte Ressourcen zugreifen , müssen Autoren auf gemeinsam genutzte Ressourcen schreiben. Diese Art von Sperre kann die Parallelität im Vergleich zu Spin-Sperren verbessern, da sie in einem Multiprozessorsystem mehreren Lesern den gleichzeitigen Zugriff auf gemeinsam genutzte Ressourcen ermöglicht und die maximal mögliche Anzahl von Lesern der tatsächlichen Anzahl logischer CPUs entspricht. Schreiber sind exklusiv. Eine Lese-/Schreibsperre kann nur einen Schreiber oder mehrere Leser gleichzeitig haben (abhängig von der Anzahl der CPUs), sie kann jedoch nicht gleichzeitig Lese- und Schreiber haben. Lese-/Schreibsperren folgen ebenfalls drei Beziehungen: Leser-Schreiber (sich gegenseitig ausschließend und synchronisiert), Leser-Leser (keine Beziehung), Schriftsteller-Schreiber (sich gegenseitig ausschließend), 2 Objekte (Leser und Schreiber), 1 Standort.
pthread_rwlock_wrlock-Schreibmodus, gibt 0 bei Erfolg zurück, Fehlercode bei Fehler
pthread_rwlock_rdlock-Lesemodus, gibt 0 bei Erfolg und Fehlercode bei Fehler zurück
pthread_rwlock_init-Initialisierung
1 #include<stdio.h> 2 #include<pthread.h> 3 #define _READNUM_ 2 4 #define _WREITENUM_ 3 5 pthread_rwlock_t lock; 6 int buf=0; 7 void* read(void* reg) 8 { 9 while(1) 10 { 11 if(pthread_rwlock_tryrdlock(&lock) != 0)//读方式 12 { 13 printf("writer is write! reader wait...\n"); 14 } 15 else 16 { 17 printf("reader is reading,val is %d\n",buf); 18 pthread_rwlock_unlock(&lock); 19 } 20 sleep(2); 21 } 22 } 23 void* write(void* reg) 24 { 25 while(1) 26 { 27 if(pthread_rwlock_trywrlock(&lock) != 0)//写方式 28 { 29 printf("reader is reading ! writer wait...\n"); 30 sleep(1); 31 } 32 else 33 { 34 buf++; 35 printf("writer is writing,val is %d\n",buf); 36 pthread_rwlock_unlock(&lock); 37 38 } 39 sleep(1); 40 } 41 } 42 int main() 43 { 44 pthread_rwlock_init(&lock,NULL); 45 pthread_t tid; 46 int i=0; 47 for(i;i< _WREITENUM_;i++) 48 { 49 pthread_create(&tid,NULL,write,NULL); 50 } 51 52 for(i; i< _READNUM_;i++) 53 { 54 pthread_create(&tid,NULL,read,NULL); 55 } 56 pthread_join(tid,NULL); 57 sleep(100); 58 return 0; 59 }