临界资源:
一个进程的资源对于运行在它内部的线程是共享的,一次只允许一个线程使用的资源叫做临界资源
临界区:
访问临界资源的那段程序叫做临界区
线程的同步:
同步就是协同步调,按照预定的先后顺序执行。
“同”字应是指协同、协助、互相配合。
线程的互斥:
某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。
但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
同步的方法(互斥量、条件变量、信号量)
互斥量(mutex)
多个线程同时访问共享数据时,经常会发生冲突,因此常常引入互斥锁来解决这个问题。
冲突:例如对一个全局变量g_var进行加一操作,这样的操作需要三条指令来完成:(1、把该变量的值从内存读到寄存器;2、寄存器进行加一操作;3、把修改后的值写回内存),因为这三条指令的执行不是原子操作,有可能线程1执行了第一步(把变量值读到寄存器),此时内核调度线程2,线程2也把该值读到寄存器,它们都进行加一操作后写回内存,导致最后结果只加了一次,而我们希望是两次。
解决:引入互斥锁,获得锁的线程可以对变量进行“读-修改-写”操作,这个操作是原子的,完成这个操作后释放锁给别的线程,没有获得锁的线程只能等待而不能访问该共享数据。
有关函数:
1.互斥锁的初始化及销毁
其中初始化既可以使用函数pthread_mutex_init(),也可以定义pthread_mutex_t类型的变量mutex,并赋值PTHREAD_MUTEX_INITIALIZER
2.对mutex的加锁及解锁
一个线程可以调用pthread_mutex_lock获得Mutex,如果这时另一个线程已经调用pthread_mutex_lock获得了该Mutex,则当前线程需要挂起等待,直到另一个线程调用pthread_mutex_unlock释放Mutex,当前线程被唤醒,才能获得该Mutex并继续执行。
如果一个线程既想获得锁,又不想挂起等待,可以调用pthread_mutex_trylock,如果Mutex已经被另一个线程获得,这个函数会失败返回EBUSY,而不会使线程挂起等待。
Mutex的lock和unlock的伪码(以x86的xchg指令为例):
不使用互斥量代码:
#include <stdio.h> #include <pthread.h> void *thread(void *arg) { int count=2000; while(count-- >0){ int var=g_var; printf("this is thread%d,g_var=%d\n",(int)arg,g_var); g_var=var+1; } } int main() { pthread_t tid1,tid2; if(pthread_create(&tid1,NULL,thread,(void*)1)!=0){ printf("creat tid1 is failed\n"); } if(pthread_create(&tid1,NULL,thread,(void*)2)!=0){ printf("creat tid1 is failed\n"); } pthread_join(tid1,NULL); pthread_join(tid2,NULL); return 0; }
运行结果:
第一次运行线程1和线程2一共加了2275次
第二次运行一共把g_var加了2000次
使用互斥量实现代码:
include <stdio.h> #include <pthread.h> //pthread_mutex_t mutex_lock=PTHREAD_MUTEX_INITIALIZER; struct info { int _var; pthread_mutex_t *_mutex_lock; }info; int g_var=0; void *thread(void *arg) { struct info* _lock=(struct info*)arg; int count=2000; while(count-- >0){ pthread_mutex_lock(_lock->_mutex_lock); int var=g_var; printf("this is thread%d,g_var=%d\n",_lock->_var,g_var); g_var=var+1; pthread_mutex_unlock(_lock->_mutex_lock); } } int main() { pthread_t tid1,tid2; pthread_mutex_t mutex_lock; int err=pthread_mutex_init(&mutex_lock,NULL); if(err!=0){ printf("%s\n",strerror(err)); } struct info info1; info1._var=1; info1._mutex_lock=&mutex_lock; if(pthread_create(&tid1,NULL,thread,&info1)!=0){ printf("creat tid1 is failed\n"); } struct info info2; info2._var=2; info2._mutex_lock=&mutex_lock; if(pthread_create(&tid2,NULL,thread,&info2)!=0){ printf("creat tid2 is failed\n"); } pthread_join(tid1,NULL); pthread_join(tid2,NULL); err=pthread_mutex_destroy(&mutex_lock); if(err!=0){ printf("%s\n",strerror(err)); } return 0; }
运行结果:
线程1和线程2共执行了4000次
总结:当一个线程已经获得锁,另一个线程需挂起等待,当该线程释放锁后,会唤醒正在等待线程。每个mutex都有一个等待队列,当线程挂起时,就加入该等待队列,状态设置为睡眠。一个线程要唤醒别的线程,只需要从等待队列中取出一项加入就绪队列并把它的状态设置为就绪。
有的时候一个线程会两次获得锁,第一次正常获得,第二次获得所得时候发现锁已经被别占用,它就会挂起,导致自己占用了锁却处于挂起状态,这样就产生了死锁。
死锁:
产生死锁的原因
1、进程资源不足
2、进程推进的顺序不合适
3、系统资源的分配不当
产生死锁的必要条件:
1、互斥条件:一个资源一次只能被一个进程使用
2、请求与保持条件:一个已经进程因请求资源而阻塞,对已获得的资源保持不放
3、不剥夺条件:进程已获得的资源,在没使用完之前,不能强行剥夺
4、循环等待条件:若干个进程之间形成一种头尾相接的循环等待资源关系
产生死锁时,以上四个必要条件一定都成立。
条件变量
生产者消费者模型:
关系:
同步
生产者<—————>消费者
互斥
互斥
生产者<—————>生产者
互斥
消费者<—————>消费者
场所:
缓冲区,下文以链表方式实现
1.单个生产者,单个消费者,且生产者和消费者访问链表的顺序是LIFO的
代码实现:
#include<stdio.h> #include<pthread.h> pthread_mutex_t _mutex_lock=PTHREAD_MUTEX_INITIALIZER; pthread_cond_t need_product=PTHREAD_COND_INITIALIZER; typedef struct List List; struct List { int _var; List *_next; }*head=NULL; //head=NULL; void* product(void *arg) { while(1){ pthread_mutex_lock(&_mutex_lock); List* p=(List*)malloc(sizeof(List)); p->_var=(rand()%2000); p->_next=head; head=p; printf("call consumer! product success,val is :%d\n",p->_var); pthread_mutex_unlock(&_mutex_lock); sleep(rand()%3); pthread_cond_signal(&need_product); } } void* consumer(void *arg) { while(1){ pthread_mutex_lock(&_mutex_lock); if(head==NULL){ pthread_cond_wait(&need_product,&_mutex_lock); } List *p=head; head=head->_next; p->_next=NULL; pthread_mutex_unlock(&_mutex_lock); printf("consumer has get protect:%d\n",p->_var); free((void*)p); p=NULL; } } int main() { int err; pthread_t p; pthread_t c; err=pthread_create(&p,NULL,product,NULL); if(err!=0){ printf("%s\n",strerror(err)); } err=pthread_create(&c,NULL,consumer,NULL); if(err!=0){ printf("%s\n",strerror(err)); } void *status; pthread_join(p,&status); printf("%s\n",status); pthread_join(c,&status); printf("%s\n",status); pthread_cond_destroy(&need_product); pthread_mutex_destroy(&_mutex_lock); return 0; }
运行结果:
==================================================================
2.单个生产者,单个消费者,且生产者和消费者访问链表的顺序是FIFO的
代码实现:
fifo_cond.c
#include <stdio.h> #include <pthread.h> #include <stdlib.h> pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER; //pthread_cond_t need_product=PTHREAD_COND_INITIALIZER; pthread_cond_t need_product; typedef int datatype; typedef struct node{ datatype _val; struct node *_next; }node,*node_p,**node_pp; typedef struct list_info { node_p _head; node_p _tail; }list_info,*list_info_p; void init_list(node_pp head,node_pp last) { *head=NULL; *last=*head; } node_p buy_node(datatype data) { node_p p=(node_p)malloc(sizeof(node)); p->_val=data; p->_next=NULL; if(p==NULL){ return NULL; } return p; } int push(node_pp list,node_pp last,datatype data) { node_p p=buy_node(data); if(p==NULL){ return -1; } if(*last==NULL){ *last=p; *list=p; } else{ (*last)->_next=p; (*last)=(*last)->_next; } return data; } int destroy_node(node_pp list,node_pp last) { if((*list)!=NULL){ node_p tmp=*list; *list=(*list)->_next; int i=tmp->_val; free(tmp); return i; } *last=NULL; return -1; } int pop(node_pp list,node_pp last) { return destroy_node(list,last); } int show_list(node_p list,node_p last) { while(list!=last){ printf("%d->",list->_val); list=list->_next; } if(last!=NULL) printf("%d\n",last->_val); } void *product(void *arg) { while(1){ datatype data=rand()%100; pthread_mutex_lock(&lock); push(&(((list_info_p)arg)->_head),&(((list_info_p)arg)->_tail),data); printf("Product success,val:%d\n",data); pthread_mutex_unlock(&lock); sleep(2); pthread_cond_signal(&need_product); } } void *consumer(void *arg) { while(1){ int ret=-1; // sleep(4); pthread_mutex_lock(&lock); while(-1==(ret=pop(&(((list_info_p)arg)->_head),&(((list_info_p)arg)->_tail)))){ pthread_cond_wait(&need_product,&lock); } pthread_mutex_unlock(&lock); printf("consumer success,val:%d\n",ret); } } int main() { pthread_cond_init(&need_product,NULL); node_p head,tail; init_list(&head,&tail); list_info _list_info; _list_info._head=head; _list_info._tail=tail; pthread_t tid1,tid2; pthread_create(&tid1,NULL,product,(void*)(&_list_info)); pthread_create(&tid2,NULL,consumer,(void*)(&_list_info)); pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_cond_destroy(&need_product); pthread_mutex_destroy(&lock); // int data=0; // while(data<10){ // push(&head,&last,data); // show_list(head,last); // data++; // } // // while(data>0){ // pop(&head,&last); // show_list(head,last); // data--; // } return 0; }
运行结果:
consumer()有sleep(4);
运行结果:
consumer()函数中没有sleep(4)这条语句
从以上两次结果可以看出消费者是按找生产产品的顺序来消费的,如果生产者生产的慢,消费者会等待
==================================================================
3.多个生产者,多个消费者
实现代码:
include <stdio.h> #include <pthread.h> #include <stdlib.h> pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER; pthread_cond_t need_product=PTHREAD_COND_INITIALIZER; pthread_cond_t need_product; typedef int datatype; typedef struct node{ datatype _val; struct node *_next; }node,*node_p,**node_pp; node_p head,tail; typedef struct list_info { node_p _head; node_p _tail; int _flag; }list_info,*list_info_p; void init_list(node_pp head,node_pp last) { *head=NULL; *last=*head; } node_p buy_node(datatype data) { node_p p=(node_p)malloc(sizeof(node)); p->_val=data; p->_next=NULL; if(p==NULL){ return NULL; } return p; } int push(node_pp list,node_pp last,datatype data) { node_p p=buy_node(data); if(p==NULL){ return -1; } if(*last==NULL){ *last=p; *list=p; } else{ (*last)->_next=p; (*last)=(*last)->_next; } return data; } int destroy_node(node_pp list,node_pp last) { if((*list)!=NULL){ node_p tmp=*list; *list=(*list)->_next; int i=tmp->_val; free(tmp); return i; } *last=NULL; return -1; } int pop(node_pp list,node_pp last) { return destroy_node(list,last); } int show_list(node_p list,node_p last) { while(list!=last){ printf("%d->",list->_val); list=list->_next; } if(last!=NULL) printf("%d\n",last->_val); } void *product(void *arg) { while(1){ datatype data=rand()%100; pthread_mutex_lock(&lock); push(&head,&tail,data); printf("Product%d put success,val:%d\n",(int)arg,data); pthread_mutex_unlock(&lock); sleep(1); // pthread_cond_signal(&need_product); pthread_cond_broadcast(&need_product); } } void *consumer(void *arg) { while(1){ int ret=-1; sleep(2); pthread_mutex_lock(&lock); while(-1==(ret=pop(&head,&tail))){ pthread_cond_wait(&need_product,&lock); } pthread_mutex_unlock(&lock); sleep(1); printf("consumer%d take success,val:%d\n",(int)arg,ret); } } int main() { init_list(&head,&tail); pthread_t tid1,tid2; pthread_create(&tid1,NULL,product,(void*)1); pthread_create(&tid2,NULL,product,(void*)2); pthread_t tid3,tid4,tid5; pthread_create(&tid3,NULL,consumer,(void*)3); pthread_create(&tid4,NULL,consumer,(void*)4); pthread_create(&tid5,NULL,consumer,(void*)5); pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_join(tid3,NULL); pthread_join(tid4,NULL); pthread_join(tid5,NULL); pthread_cond_destroy(&need_product); pthread_mutex_destroy(&lock); return 0; }
运行结果:
以上有2个生产者,3个消费者 生产者生产出的数据放入同一链表中,消费者也都从该链表取数据,任何一刻对象对改链表进行操作时,别的对象都不能对该链表进行操作,实现了互斥功能。
本文出自 “零蛋蛋” 博客,请务必保留此出处http://lingdandan.blog.51cto.com/10697032/1766418
原文地址:http://lingdandan.blog.51cto.com/10697032/1766418