- 假设有一个生产者线程,一个消费者线程,生产一个,消费一个。我们来看看怎么实现。
#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
这样就差不多了哈,确实快了呢!!!嘿嘿
参考:
- 操作系统导论