Maison >développement back-end >tutoriel php >Linux - La variable de condition (variable de condition) implémente le modèle producteur-consommateur et le verrouillage en lecture-écriture
1. Variables de condition
Pendant le processus de synchronisation des threads, il existe également les situations suivantes : le thread A doit attendre qu'une certaine condition soit établie avant de pouvoir continuer l'exécution. Si la condition n'est pas établie, le thread A. se bloquera pendant l'exécution du thread B. Lorsque cette condition est établie pendant le processus, le thread A est réveillé pour continuer l'exécution. Utilisez les variables de condition dans la bibliothèque Pthread pour bloquer l'attente d'une condition ou pour réveiller le thread en attente de cette condition. Les variables de condition sont représentées par des variables de type pthread_cond_t.
Utilisez pthread_cond_init pour initialiser la variable de condition. Si la variable de condition est allouée statiquement, vous pouvez également utiliser la définition de macro PTHEAD_COND_INITIALIZER pour l'initialiser, et utiliser pthread_cond_destroy pour détruire la variable de condition ; un numéro d'erreur est renvoyé en cas d'échec.
Une variable de condition est toujours utilisée avec un Mutex. Un thread peut appeler pthread_cond_wait pour bloquer et attendre une variable de condition. Cette fonction effectue les trois étapes suivantes :
1. Libérez le Mutex
2. Une fois réveillé, réacquérir le Mutex et. return
Un thread peut appeler pthread_cond_signal pour réveiller un autre thread en attente sur une certaine variable de condition, ou il peut appeler pthread_cond_broadcast pour réveiller tous les threads en attente sur cette variable de condition.
2. Utiliser le modèle producteur-consommateur pour illustrer
Comme son nom l'indique, on voit que pour mettre en œuvre ce modèle, il faut d'abord avoir deux rôles (producteur, consommateur). bien sûr, il doit y avoir une occasion pour que les deux ressources critiques soient accessibles (une occasion), et la relation entre producteurs (exclusion mutuelle) et la relation entre consommateurs (exclusion mutuelle) doit être comprise, la relation entre producteurs et consommateurs (synchronisation). et exclusion mutuelle), en général, est un lieu, deux rôles et trois relations. Pour l'implémenter avec du code, le producteur produit une donnée puis envoie un signal au consommateur pour qu'il la consomme, il envoie un signal au producteur pour lui dire de continuer à produire, et ainsi de suite.
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 }Résumé : Le code ci-dessus met en œuvre un seul producteur et un seul consommateur, le modèle producteur-consommateur. En termes simples, il s'agit de parvenir à une exclusion mutuelle entre producteurs et consommateurs. et une relation synchronisée et mutuellement exclusive entre producteurs et consommateurs.
3. Utilisez le sémaphore pour implémenter le modèle producteur-consommateur
La variable Mutex est soit 0, soit 1, ce qui peut être considéré comme la quantité disponible d'une ressource. Lorsqu'elle est initialisée, Mutex est 1, indiquant qu'il existe. une ressource disponible. ,
Obtenez la ressource lors du verrouillage, réduisez Mutex à 0, indiquant qu'il n'y a plus de ressource disponible, libérez la ressource lors du déverrouillage et augmentez Mutex à 1, indiquant qu'il y a une autre ressource disponible. Le sémaphore est similaire au Mutex et représente le nombre de ressources disponibles. Contrairement à Mutex, ce nombre peut être supérieur à 1. Autrement dit, si le nombre de ressources décrites par le sémaphore est de 1, le sémaphore et le verrouillage mutex à ce moment sont les mêmes !
sem_init() initialise le sémaphore
l'opération sem_wait()P obtient des ressources
l'opération sem_post()V libère des ressources
sem_destroy() détruit le sémaphore
Ce qui précède est un modèle producteur-consommateur écrit dans une liste chaînée, et son espace est alloué dynamiquement. Maintenant, le modèle producteur-consommateur est réécrit sur la base d'une file d'attente en anneau de taille fixe
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. . Verrouillage de lecture et d'écriture
Le verrouillage de lecture-écriture est en fait un verrou rotatif spécial, utilisé pour gérer la situation de plus de lecture et de moins d'écriture. Il divise les visiteurs des ressources partagées en lecteurs et écrivains qui lisent uniquement les ressources partagées. , les écrivains doivent écrire sur des ressources partagées. Ce type de verrou peut améliorer la concurrence par rapport aux verrous tournants, car dans un système multiprocesseur, il permet à plusieurs lecteurs d'accéder aux ressources partagées en même temps, et le nombre maximum possible de lecteurs correspond au nombre réel de processeurs logiques. Les écrivains sont exclusifs. Un verrou en lecture-écriture ne peut avoir qu'un seul écrivain ou plusieurs lecteurs en même temps (en fonction du nombre de processeurs), mais il ne peut pas avoir à la fois des lecteurs et des écrivains. Les verrous en lecture-écriture suivent également trois relations : lecteur-écrivain (mutuellement exclusif et synchronisé), lecteur-lecteur (aucune relation), écrivain-écrivain (mutuellement exclusif) 2 objets (lecteur et écrivain), 1 emplacement.
pthread_rwlock_wrlock Mode écriture, renvoie 0 en cas de succès, code d'erreur en cas d'échec
pthread_rwlock_rdlock Mode lecture, renvoie 0 en cas de succès, code d'erreur en cas d'échec
initialisation pthread_rwlock_init
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 }Ce qui précède est le contenu de Linux - Variable de condition (variable de condition) pour implémenter le modèle producteur-consommateur et le verrouillage en lecture-écriture. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois (www.php.cn). !