- 假设有一个生产者线程,一个消费者线程,生产一个,消费一个。我们来看看怎么实现。
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> int buffer; int count = 0; void put(int value) { assert(count == 0); count = 1; buffer = value; } int get() { assert(count == 1); count = 0; return buffer; } void *producer(void *arg) { printf("producer...\n"); int i; int loops = (int) arg; for (i = 0; i < loops; i++) { put(i); printf("put:%d\n",i); } } void *consumer(void *arg) { printf("consumer...\n"); int i; while(1) { int tmp = get(); printf("%d\n",tmp); } } int main() { printf("begin...\n"); pthread_t p; pthread_create(&p, NULL, consumer,(void *)100); producer((void*)100); pthread_join(p, NULL); return 0; }
上面的代码运行,显然不能满足我们要求,这个时候就需要条件变量。
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> int buffer; int count = 0; pthread_cond_t cond; pthread_mutex_t mutex; void put(int value) { assert(count == 0); count = 1; buffer = value; } int get() { assert(count == 1); count = 0; return buffer; } void *producer(void *arg) { printf("producer...\n"); int i; int loops = (int) arg; for (i = 0; i < loops; i++) { pthread_mutex_lock(&mutex); if(count == 1) pthread_cond_wait(&cond, &mutex); put(i); pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); printf("put:%d\n",i); } } void *consumer(void *arg) { printf("consumer...\n"); int i; int loops = (int)arg; for(i =0; i < loops; i++) { pthread_mutex_lock(&mutex); if(count == 0) pthread_cond_wait(&cond, &mutex); int tmp = get(); printf("%d\n",tmp); pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); } }
[zf@localhost ch30]$ ./a.out begin... producer... put:0 consumer... 0 put:1 1 put:2 2 put:3 3 put:4 4
结果来看是满足要求的,可是如果有两个消费者呢?
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> int buffer; int count = 0; pthread_cond_t cond; pthread_mutex_t mutex; void put(int value) { assert(count == 0); count = 1; buffer = value; } int get() { assert(count == 1); count = 0; return buffer; } void *producer(void *arg) { printf("producer...\n"); int i; int loops = (int) arg; for (i = 0; i < loops; i++) { pthread_mutex_lock(&mutex); if(count == 1) pthread_cond_wait(&cond, &mutex); put(i); pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); printf("put: %d",i); } } void *consumer(void *arg) { printf("consumer...\n"); int i; int loops = (int)arg; for(i =0; i < loops; i++) { pthread_mutex_lock(&mutex); if(count == 0) pthread_cond_wait(&cond, &mutex); int tmp = get(); printf("[%d]get: %d\n",pthread_self(),tmp); pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); } } int main() { printf("begin...\n"); pthread_t p,p1; pthread_create(&p, NULL, consumer,(void *)10000); pthread_create(&p1, NULL, consumer, (void*)10000); producer((void*)20000); pthread_join(p, NULL); pthread_join(p1, NULL); return 0; }
put: 1207[1848375040]get: 1207 put: 1208[1839982336]get: 1208 put: 1209[1848375040]get: 1209 put: 1210[1839982336]get: 1210 put: 1211[1848375040]get: 1211 a.out: main.c:21: get: Assertion `count == 1' failed. 已放弃(吐核)
我们发现失败了,这是为什么呢?我们来思考一下,首先和if语句有关系。就是消费者Tc1被信号唤醒的时候,刚好被Tc2抢占,消耗了缓存,导致Tc1再消耗的时候会触发断言,这个问题应该怎么办呢?其实很简单,用while代替if,我们再次检查一下容量。
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> int buffer; int count = 0; pthread_cond_t cond; pthread_mutex_t mutex; void put(int value) { assert(count == 0); count = 1; buffer = value; } int get() { assert(count == 1); count = 0; return buffer; } void *producer(void *arg) { printf("producer...\n"); int i; int loops = (int) arg; for (i = 0; i < loops; i++) { pthread_mutex_lock(&mutex); while(count == 1) pthread_cond_wait(&cond, &mutex); put(i); pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); printf("put: %d",i); } } void *consumer(void *arg) { printf("consumer...\n"); int i; int loops = (int)arg; for(i =0; i < loops; i++) { pthread_mutex_lock(&mutex); while(count == 0) pthread_cond_wait(&cond, &mutex); int tmp = get(); printf("[%d]get: %d\n",pthread_self(),tmp); pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); } } int main() { printf("begin...\n"); pthread_t p,p1; pthread_create(&p, NULL, consumer,(void *)10000); pthread_create(&p1, NULL, consumer, (void*)10000); producer((void*)20000); pthread_join(p, NULL); pthread_join(p1, NULL); return 0; }
put: 1276[1475249920]get: 1276 put: 1277[1466857216]get: 1277 put: 1278[1475249920]get: 1278 put: 1279[1466857216]get: 1279 put: 1280[1475249920]get: 1280 put: 1281[1466857216]get: 1281 put: 1282[1466857216]get: 1282 put: 1283[1475249920]get: 1283 put: 1284[1466857216]get: 1284 put: 1285[1466857216]get: 1285
可是结果并非预期的那样,发现程序执行到一半不动了,卡住了。这是为什么呢?我们加打印看一下
void *producer(void *arg) { printf("producer...\n"); int i; int loops = (int) arg; for (i = 0; i < loops; i++) { pthread_mutex_lock(&mutex); while(count == 1){ printf("producer wait......\n"); pthread_cond_wait(&cond, &mutex); } put(i); pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); printf("put: %d",i); } } void *consumer(void *arg) { printf("consumer...\n"); int i; int loops = (int)arg; for(i =0; i < loops; i++) { pthread_mutex_lock(&mutex); while(count == 0){ printf("consumer wait.....\n"); pthread_cond_wait(&cond, &mutex); } int tmp = get(); printf("[%d]get: %d\n",pthread_self(),tmp); pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); } }
put: 197producer wait...... [632694528]get: 197 consumer wait..... consumer wait.....
这个发现一个问题,就是消费者T1消费后,唤醒的是T2,这样就三个线程都在休眠,这个真是个可怕的问题,我们应该怎么办呢?按道理,消费者消费后应该唤醒生产者,所以消费者和生产者应该发出不同信号,这样不就好了吗
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> int buffer; int count = 0; pthread_cond_t empty,fill; pthread_mutex_t mutex; void put(int value) { assert(count == 0); count = 1; buffer = value; } int get() { assert(count == 1); count = 0; return buffer; } void *producer(void *arg) { printf("producer...\n"); int i; int loops = (int) arg; for (i = 0; i < loops; i++) { pthread_mutex_lock(&mutex); while(count == 1){ printf("producer wait......\n"); pthread_cond_wait(&empty, &mutex); } put(i); pthread_cond_signal(&fill); pthread_mutex_unlock(&mutex); printf("put: %d",i); } } void *consumer(void *arg) { printf("consumer...\n"); int i; int loops = (int)arg; for(i =0; i < loops; i++) { pthread_mutex_lock(&mutex); while(count == 0){ printf("consumer wait.....\n"); pthread_cond_wait(&fill, &mutex); } int tmp = get(); printf("[%d]get: %d\n",pthread_self(),tmp); pthread_cond_signal(&empty); pthread_mutex_unlock(&mutex); } } int main() { printf("begin...\n"); pthread_t p,p1; pthread_create(&p, NULL, consumer,(void *)10000); pthread_create(&p1, NULL, consumer, (void*)10000); producer((void*)20000); pthread_join(p, NULL); pthread_join(p1, NULL); return 0; }
put: 19997producer wait...... [754308864]get: 19997 consumer wait..... put: 19998producer wait...... [754308864]get: 19998 consumer wait..... put: 19999[754308864]get: 19999
这里,两个问题就都解决了,是不是很happy。别高兴太早,这个方案虽然可用了,但不太通用,我们buffer不可能只是一个的,下面我们再改改
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> #define MAX 1000 int buffer[MAX]; int fills = 0; int use = 0; int count = 0; pthread_cond_t empty,fill; pthread_mutex_t mutex; void put(int value) { buffer[fills] = value; fills = (fills + 1) % MAX; count++; } int get() { int tmp = buffer[use]; use = (use + 1) % MAX; count--; return tmp; } void *producer(void *arg) { printf("producer...\n"); int i; int loops = (int) arg; for (i = 0; i < loops; i++) { pthread_mutex_lock(&mutex); while(count == MAX){ printf("producer wait......\n"); pthread_cond_wait(&empty, &mutex); } put(i); pthread_cond_signal(&fill); pthread_mutex_unlock(&mutex); printf("put: %d",i); } } void *consumer(void *arg) { printf("consumer...\n"); int i; int loops = (int)arg; for(i =0; i < loops; i++) { pthread_mutex_lock(&mutex); while(count == 0){ printf("consumer wait.....\n"); pthread_cond_wait(&fill, &mutex); } int tmp = get(); printf("[%d]get: %d\n",pthread_self(),tmp); pthread_cond_signal(&empty); pthread_mutex_unlock(&mutex); } } int main() { printf("begin...\n"); pthread_t p,p1; pthread_create(&p, NULL, consumer,(void *)10000); pthread_create(&p1, NULL, consumer, (void*)10000); producer((void*)20000); pthread_join(p, NULL); pthread_join(p1, NULL); return 0; }
[-797354240]get: 19988 [-797354240]get: 19989 [-797354240]get: 19990 [-797354240]get: 19991 [-797354240]get: 19992 [-797354240]get: 19993 [-797354240]get: 19994 [-797354240]get: 19995 consumer wait..... put: 19996put: 19997put: 19998put: 19999[-797354240]get: 19996 [-797354240]get: 19997 [-797354240]get: 19998 [-797354240]get: 19999
这样就差不多了哈,确实快了呢!!!嘿嘿
参考:
- 操作系统导论