1.互斥锁和条件变量实现,没有限制缓冲区大小
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <queue>
using namespace std;
queue<int> tq;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *consumer(void *arg)
{
while(true)
{
pthread_mutex_lock(&mutex);
while(tq.empty())//用while防止虚假唤醒
{
pthread_cond_wait(&cond,&mutex);//当缓冲区为空就等待信号
}
int m=tq.front();
tq.pop();
pthread_mutex_unlock(&mutex);
printf("%ld th thread get-%d- message!\n",(long)arg,m);//输出语句放外边,因为锁的粒度越小越好
usleep(1000);
}
}
void *producer(void *arg)//multiple productors
{
while(true)
{
pthread_mutex_lock(&mutex);
tq.push((int)(long)arg);
pthread_mutex_unlock(&mutex);
pthread_cond_broadcast(&cond);//生产数据就广播数据
usleep(15000);
}
}
void producer2()//single productor
{
int i=0;
while(true)
{
pthread_mutex_lock(&mutex);
tq.push(i++);
pthread_mutex_unlock(&mutex);
pthread_cond_broadcast(&cond);
usleep(10000);
}
}
int main()
{
pthread_t c1,c2,c3;
pthread_t p1,p2;
pthread_create(&c1,NULL,consumer,(void *)1);
pthread_create(&c2,NULL,consumer,(void *)2);
pthread_create(&c3,NULL,consumer,(void *)3);
pthread_create(&p1,NULL,producer,(void *)1);
pthread_create(&p2,NULL,producer,(void *)2);
//producter2();
pthread_join(c1,NULL);
pthread_join(c2,NULL);
pthread_join(c3,NULL);
pthread_join(p1,NULL);
pthread_join(p2,NULL);
return 0;
}
运行结果
PS:用while条件判断而不是if是为了防止虚假唤醒
虚假唤醒一是操作系统可能出现内部问题错误地唤醒了条件变量,二是生产者广播时给所有线程等待的条件变量都发了信号,但是有一个线程在另外的线程还没执行完唤醒的原子操作时就消费了数据,结果缓冲区为空,导致另外线程被唤醒执行取数据的操作时就取了个寂寞。所以while循环判断即可
2.利用信号量(PV操作)和互斥锁实现,可以限制缓冲区大小
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <queue>
#include <semaphore.h>
using namespace std;
int msgid=1;
struct message
{
int id;
int pid;
};
queue<message> q;
sem_t Empty,Full;
pthread_mutex_t mutex;
void *consumer(void *arg)
{
int c_id=(int)(long)arg;
while(true)
{
sem_wait(&Full);//P操作 Full--
pthread_mutex_lock(&mutex);
message tmp=q.front();
q.pop();
pthread_mutex_unlock(&mutex);
sem_post(&Empty);//V操作 Empty++
printf("-%d-th consumer get id-%d- pid-%d- message\n",c_id,tmp.id,tmp.pid);
sleep(2);
}
}
void *producer(void *arg)
{
int p_id=(int)(long)arg;
while(true)
{
sem_wait(&Empty);//P操作 Empty--
pthread_mutex_lock(&mutex);//上锁的顺序不能互换
q.push({msgid++,p_id});
pthread_mutex_unlock(&mutex);
sem_post(&Full);//V操作 Full++
sleep(1);
}
}
void *monitor(void *arg)//观察线程
{
while(true)
{
printf("--size of q is -%d-\n",(int)q.size());
sleep(1);
}
}
int main()
{
pthread_t c1,c2,c3;
pthread_t p1,p2,p3;
pthread_t m1;
sem_init(&Empty,0,5);//队列限制大小为5
sem_init(&Full,0,0);
pthread_create(&m1,NULL,monitor,NULL);
pthread_create(&c1,NULL,consumer,(void *)1);
pthread_create(&c2,NULL,consumer,(void *)2);
pthread_create(&c3,NULL,consumer,(void *)3);
pthread_create(&p1,NULL,producer,(void *)1);
pthread_create(&p2,NULL,producer,(void *)2);
pthread_create(&p3,NULL,producer,(void *)3);
pthread_join(m1,NULL);
pthread_join(c1,NULL);
pthread_join(c2,NULL);
pthread_join(c3,NULL);
pthread_join(p1,NULL);
pthread_join(p2,NULL);
pthread_join(p3,NULL);
return 0;
}
运行结果(可以看到缓冲区大小最大值为5)
积跬步,至千里。
多线程编程学习从消费者和生产者问题开始吧!