生产者的工作是制造数据,只有缓冲区没满时,生产者才能把消息放入到缓冲区,否则必须等待; 同时,只有缓冲区不空时,消费者才能从中取出消息,一次消费一段数据(即将其从缓存中移出),否则必须等待。由于缓冲区是临界资源,它只允许一个生产者放入消息,或者一个消费者从中取出消息,同时只允许一个生产者进行制造,多个生产者不能同时制造,也只允许一个消费之进行消费,多个消费者不能同时进行消费
条件变量+互斥锁实现生产者消费者模型
- 条件本身不是锁!但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。
- 使用互斥量保护共享数据;
- 使用条件变量可以使线程阻塞, 等待某个条件的发生, 当条件满足的时候解除阻塞.
- 条件变量的两个动作:
- 条件不满足, 阻塞线程
- 条件满足, 通知阻塞的线程解除阻塞, 开始工作.
- 条件变量相关函数
- pthread_cond_t cond;
- 作用:定义一个条件变量
- int pthread_cond_init(pthread_cond_t *restrict cond , const pthread_condattr_t *restrict attr);
- 函数描述:初始化条件变量
- 函数参数: cond: 条件变量 attr: 条件变量属性, 通常传NULL
- pthread_cond_t cond;
- 函数返回值:成功返回0, 失败返回错误号
- int pthread_cond_destroy(pthread_cond_t *cond);
- 函数描述: 销毁条件变量
- 函数参数: 条件变量
- 返回值: 成功返回0, 失败返回错误号
- int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
- 函数描述: 条件不满足, 引起线程阻塞并解锁;条件满足, 解除线程阻塞, 并加锁
- 函数参数:
- cond: 条件变量
- mutex: 互斥锁变量
- 函数返回值: 成功返回0, 失败返回错误号
- int pthread_cond_signal(pthread_cond_t *cond);
- 函数描述: 唤醒至少一个阻塞在该条件变量上的线程
- 函数参数: 条件变量
- 函数返回值: 成功返回0, 失败返回错误号
1 //使用条件变量实现生产者和消费者模
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <sys/types.h>
6 #include <unistd.h>
7 #include <pthread.h>
8 typedef struct node
9 {
10 int data;
11 struct node *next;
12 }NODE;
13
14 NODE *head = NULL;
15
16 //定义一把锁
17 pthread_mutex_t mutex;
18
19 //定义条件变量
20 pthread_cond_t cond;
21
22 //生产者线程
23 void *producer(void *arg)
24 {
25 NODE *pNode = NULL;//节点初始化
26 int n = *(int *)arg;
27 while(1)
28 {
29 //生产一个节点
30 pNode = (NODE *)malloc(sizeof(NODE));
31 if(pNode==NULL)
32 {
33 perror("malloc error");
34 exit(-1);
35 }
36 pNode->data = rand()%1000;//随机生成0~999的数字作为节点的值
37 printf("P[%d]:[%d]\n", n, pNode->data);
38
39 //加锁
40 pthread_mutex_lock(&mutex);
41
42 pNode->next = head;
43 head = pNode;
44
45 //解锁
46 pthread_mutex_unlock(&mutex);
47
48 //通知消费者线程解除阻塞
49 pthread_cond_signal(&cond);
50
51 sleep(rand()%3);
52 }
53 }
54
55
56 //消费者线程
57 void *consumer(void *arg)
58 {
59 NODE *pNode = NULL;
60 int n = *(int *)arg;
61 while(1)
62 {
63 //加锁
64 pthread_mutex_lock(&mutex);
65
66 if(head==NULL)
67 {
68 //若条件不满足,需要阻塞等待并解锁
69 //若条件满足(被生成者线程调用pthread_cond_signal函数通知),解除阻塞并加锁
70 pthread_cond_wait(&cond, &mutex);
71 }
72
73 if(head==NULL)
74 {
75 //解锁
76 pthread_mutex_unlock(&mutex);
77 continue;
78 }
79
80 printf("C[%d]:[%d]\n", n, head->data);
81 pNode = head;
82 head = head->next;
83
84 //解锁
85 pthread_mutex_unlock(&mutex);
86
87 free(pNode);
88 pNode = NULL;
89
90 sleep(rand()%3);
91 }
92 }
93
94 int main()
95 {
96 int ret;
97 int i = 0;
98 pthread_t thread1[5];
99 pthread_t thread2[5];
100
101 //初始化互斥锁
102 pthread_mutex_init(&mutex, NULL);
103
104 //条件变量初始化
105 pthread_cond_init(&cond, NULL);
106
107 int arr[5];
108 for(i=0; i<5; i++)
109 {
110 arr[i]= i;
111 //创建生产者线程
112 ret = pthread_create(&thread1[i], NULL, producer, &arr[i]);
113 if(ret!=0)
114 {
115 printf("pthread_create error, [%s]\n", strerror(ret));
116 return -1;
117 }
118
119 //创建消费者线程
120 ret = pthread_create(&thread2[i], NULL, consumer, &arr[i]);
121 if(ret!=0)
122 {
123 printf("pthread_create error, [%s]\n", strerror(ret));
124 return -1;
125 }
126 }
127
128 //等待线程结束
129 for(i=0; i<5; i++)
130 {
131 pthread_join(thread1[i], NULL);
132 pthread_join(thread2[i], NULL);
133 }
134
135 //释放互斥锁
136 pthread_mutex_destroy(&mutex);
137
138 //释放条件变量
139 pthread_cond_destroy(&cond);
140
141 return 0;
142 }
执行结果:
信号量实现生产者消费者模型
信号量介绍
信号量相当于多把锁, 可以理解为是加强版的互斥锁
2 相关函数
- 定义信号量 sem_t sem;//头文件#include <semaphore.h>
- int sem_init(sem_t *sem, int pshared, unsigned int value);
- 函数描述: 初始化信号量
- 函数参数: sem: 信号量变量 pshared: 0表示线程同步, 1表示进程同步value: 最多有几个线程操作共享数据
- 函数返回值:成功返回0, 失败返回-1, 并设置errno值
- int sem_wait(sem_t *sem);
- 函数描述: 调用该函数一次, 相当于sem--, 当sem为0的时候, 引起阻塞
- 函数参数: 信号量变量
- 函数返回值: 成功返回0, 失败返回-1, 并设置errno值
- int sem_post(sem_t *sem);
- 函数描述: 调用一次, 相当于sem++
- 函数参数: 信号量变量
- 函数返回值: 成功返回0, 失败返回-1, 并设置errno值
- int sem_trywait(sem_t *sem);
- 函数描述: 尝试加锁, 若失败直接返回, 不阻塞
- 函数参数: 信号量变量
- 函数返回值: 成功返回0, 失败返回-1, 并设置errno值
- int sem_destroy(sem_t *sem);
- 函数描述: 销毁信号量
- 函数参数: 信号量变量
- 函数返回值: 成功返回0, 失败返回-1, 并设置errno值
1 //使用信号量实现生产者和消费者模
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <sys/types.h>
6 #include <unistd.h>
7 #include <pthread.h>
8 #include <semaphore.h>
9
10 typedef struct node
11 {
12 int data;
13 struct node *next;
14 }NODE;
15
16 NODE *head = NULL;
17
18 //定义信号量
19 sem_t sem_producer;
20 sem_t sem_consumer;
21
22 //生产者线程
23 void *producer(void *arg)
24 {
25 NODE *pNode = NULL;
26 while(1)
27 {
28 //生产一个节点
29 pNode = (NODE *)malloc(sizeof(NODE));
30 if(pNode==NULL)
31 {
32 perror("malloc error");
33 exit(-1);
34 }
35 pNode->data = rand()%1000;
36
37 //阻塞
38 sem_wait(&sem_producer);
39 pNode->next = head;
40 head = pNode;
41
42 printf("P[%d]\n", pNode->data);
43
44 //解阻塞给消费者
45 sem_post(&sem_consumer);
46
47 sleep(rand()%3);
48 }
49 }
52 //消费者线程
53 void *consumer(void *arg)
54 {
55 NODE *pNode = NULL;
56 while(1)
57 {
58
61
62 printf("C[%d]\n", head->data);
63 pNode = head;
64 head = head->next;
65
66 free(pNode);
67 pNode = NULL;
68
69 //解开阻塞给生产者
70 sem_post(&sem_producer);
71
72 sleep(rand()%3);
73 }
74 }
75
76 int main()
77 {
78 int ret;
79 pthread_t thread1;
80 pthread_t thread2;
81
82 //初始化信号量
83 sem_init(&sem_producer, 0, 5);
84 sem_init(&sem_consumer, 0, 0);
85
86 //创建生产者线程
87 ret = pthread_create(&thread1, NULL, producer, NULL);
88 if(ret!=0)
89 {
90 printf("pthread_create error, [%s]\n", strerror(ret ));
91 return -1;
92 }
93
94 //创建消费者线程
95 ret = pthread_create(&thread2, NULL, consumer, NULL);
96 if(ret!=0)
97 {
98 printf("pthread_create error, [%s]\n", strerror(ret ));
99 return -1;
100 }
101
102 //等待线程结束
103 pthread_join(thread1, NULL);
104 pthread_join(thread2, NULL);
105
106 //释放信号量资源
107 sem_destroy(&sem_producer);
108 sem_destroy(&sem_consumer);
109
110 return 0;
111 }