一、条件变量
在线程同步过程中还有如下的情况:线程A需要等某个条件成立之后才能继续往下执行,如果条件不成立,线程A就阻塞,而线程B在执行过程中使这个条件成立了,就唤醒线程A继续执行。在Pthread库中用条件变量阻塞等待一个条件,或者唤醒等待这个条件的线程。条件变量用pthread_cond_t类型的变量来表示。
用pthread_cond_init 初始化条件变量、如果条件变量是静态分配的,也可以用宏定义 PTHEAD_COND_INITIALIZER初始化,用pthread_cond_destroy 销毁条件变量;成功返回0,失败返回错误号。
一个条件变量总是和一个Mutex搭配使用的。一个线程可以调用pthread_cond_wait在一个条件变量上阻塞等待,这个函数做以下三步操作:
1. 释放Mutex
2. 阻塞等待
3. 当被唤醒时,重新获得Mutex并返回
一个线程可以调用pthread_cond_signal唤醒在某个条件变量上等待的另一个线程,也可以调用pthread_cond_broadcast唤醒在这个条件变量上等待的所有线程。
二、用生产者-消费者模型来说明
顾名思义,可以看出要实现这个模型,首先得有两个角色(生产者,消费者),有了两个角色之外当然该得有一个场合让两个都能访问到的临界资源(一个场合),还得弄明白生产者与生产者之间的关系(互斥),消费者与消费者之间的关系(互斥),生产者和消费者之间的关系(同步与互斥),总的来说就是一个场所,两个角色,三种关系。用代码来实现,生产者生产一个数据,然后发出信号让,消费者消费,消费者消费完之后给生产者发信号告诉生产者让生产者继续生产,如此重复。
1 #include
2 #include
3 #include
4 #include
5 #include
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 }
总结:上面代码实现的是单生产者和单消费者,生产者-消费者模型,简单的来说就是要实现生产者与生产者之间互斥,消费者与消费者之间互斥,生产者与消费者之间同步互斥的关系。
三、用信号量实现生产者-消费者模型
Mutex变量是非0即1的,可看作一种资源的可用数量,初始化时Mutex是1,表示有一个可用资源,
加锁时获得该资源,将Mutex减到0,表示不再有可用资源,解锁时释放该资源,将Mutex重新加到1,表示又有了一个可用资源。信号量(Semaphore)和Mutex类似,表示可用资源的数量,和Mutex不同的是这个数量可以大于1。即,如果信号量描述的资源数目是1时,此时的信号量和互斥锁相同!
sem_init()初始化信号量
sem_wait()P操作获得资源
sem_post()V操作释放资源
sem_destroy()销毁信号量
上面是用链表写的生产者-消费者模型,其空间是动态分配的,现在基于固定大小的环形队列重写生产者-消费者模型1 #include
2 #include
3 #include
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 }
四、读写锁
读写锁实际是一种特殊的自旋锁,用来处理读多写少的情况,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者是排他性的,一个读写锁同时只能有一个写者或多个读者(与CPU数相关),但不能同时既有读者又有写者。读写锁也遵循3种关系:读者-写者(互斥与同步)、读者-读者(无关系)、写者-写者(互斥)2个对象(读者和写者),1个场所。
pthread_rwlock_wrlock 写方式,成功返回0,失败返回错误码
pthread_rwlock_rdlock 读方式,成功返回0,失败返回错误码
pthread_rwlock_init初始化1 #include
2 #include
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
48 {
49 pthread_create(&tid,NULL,write,NULL);
50 }
51
52 for(i; i
53 {
54 pthread_create(&tid,NULL,read,NULL);
55 }
56 pthread_join(tid,NULL);
57 sleep(100);
58 return 0;
59 }