#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#define MAX_STOCK 20 //仓库容量
char g_storage[MAX_STOCK];
size_t g_stock=0; //当前库存
//互斥量
pthread_mutex_t g_mtx=PTHREAD_MUTEX_INITIALIZER;
//条件变量
pthread_cond_t g_full=PTHREAD_COND_INITIALIZER;//满仓
pthread_cond_t g_empty=PTHREAD_COND_INITIALIZER;//空仓
//显示库存
void show(const char *who,const char *op,char prod)
{
printf("%s:",who);
size_t i;
for(i=0;i<g_stock;++i)
{
printf("%c",g_storage[i]);
}
printf("%s%c\n",op,prod);
}
//生产者线程
void *produce(void *arg)
{
const char *who=(const char*)arg;
for(;;)
{
pthread_mutex_lock(&g_mtx); //上锁,独享g_stock内部资源
if(g_stock>=MAX_STOCK) //生产数量大于20
{
printf("%s:OKOKOKOKOKOKOKOKOKOKOK\n",who);
//pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex)
//函数中的第二个参数是一个互斥量,用于对条件变量保护
//该函数会将调用线程阻塞在cond这个条件变量上,将它放在等待条件的线程列表中,
//同时对互斥锁解锁,是的其它线程可以访问资源
pthread_cond_wait(&g_full,&g_mtx); //库存太多,生产太快,将生产者线程阻塞在满仓条件变量上。release mutex
}
char prod='A'+rand()%26;
show(who,"<-",prod);
g_storage[g_stock++]=prod;
//pthread_cond_signal()用来给阻塞的线程发送信号来唤醒等待cond条件的线程。
pthread_cond_signal(&g_empty);
pthread_mutex_unlock(&g_mtx);
usleep((rand()%100)*1000);
}
return NULL;
}
//消费者线程
void *customer(void *arg)
{
const char *who=(const char*)arg;
for(;;)
{
pthread_mutex_lock(&g_mtx); //上锁
if(!g_stock)
{
printf("%s:NONONONONONONO!\n",who);
pthread_cond_wait(&g_empty,&g_mtx); //没库存,消费太快,将消费者线程堵塞在空仓条件变量上,
}
char prod=g_storage[--g_stock];
show(who,"->",prod);
pthread_cond_signal(&g_full); //通知生产者可以进行生产了
pthread_mutex_unlock(&g_mtx); //解锁
usleep((rand()%100)*1000);
}
return NULL;
}
int main()
{
srand(time(NULL));
pthread_attr_t attr;
pthread_attr_init(&attr);
//对线程设置分离属性状态
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
pthread_t tid;
pthread_create(&tid,&attr,produce,NULL);
pthread_create(&tid,&attr,customer,NULL);
getchar();
return 0;
}
首先,生产者线程和消费者线程各自独立执行,我们会遇到两种特殊的情况,1.生产者效率较高使得仓库满;2.消费者效率较高使得仓库空。这两种情况中的任意一种都会导致线程阻塞并占用资源(为了保证同步,我们使用了互斥锁,这使得在线程阻塞后,其它线程拿不到资源)。而通过使用条件变量可以使得当某个线程出现了任意的两种情况中的一种,该线程阻塞,通过另一个线程改变条件之后,给阻塞的线程发送信号来唤醒阻塞的线程。
接下来我们要说的是pthread_cond_wait和pthread_cond_signal这两个函数。
pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex)函数中的第二个参数是一个互斥量,用于对条件变量保护。该函数会将调用线程阻塞在cond这个条件变量上,将它放在等待条件的线程列表中,同时对互斥锁解锁,是的其它线程可以访问资源,在得到其它线程对于cond所发出的信号后,该函数立马返回,并再次加锁。
pthread_cond_signal(pthread_cond_t *restrict cond)这个函数就是用来给阻塞的线程发送信号来唤醒等待cond条件的线程。
通过这两个函数便实现了线程间的同步。而这两个函数的实际功能就是一个阻塞线程,另一个唤醒线程。
对照这上面的生产者消费者模型就是例如当生产者生产过剩,使得仓库满,此时生产者线程阻塞在g_full这个条件变量上,而消费者线程消费后改变了条件状态,通过发送信号来唤醒等待g_full条件的线程,此时生产者又可以继续生产。
执行结果 g++ pthread.cpp -o pthread -lpthread
./pthread
(执行结果如下:
(null):UAC<-D
(null):UAC->D
(null):UA->C
(null):UA<-F
(null):UA->F
(null):UA<-W
(null):UA->W
(null):U->A
(null):U<-S
(null):U->S
(null):U<-P
(null):U->P
(null):->U
(null):NONONONONONONO!
(null):<-T
(null):->T
(null):<-Q
(null):->Q
(null):<-U
(null):U<-O
(null):UO<-Y
(null):UO->Y
(null):U->O
(null):->U
(null):<-H
(null):->H
(null):NONONONONONONO!
(null):<-C
(null):->C
(null):NONONONONONONO!
(null):<-Z
(null):->Z
(null):<-T
(null):T<-B
(null):T->B
(null):->T
(null):<-P
(null):->P
(null):NONONONONONONO!
(null):<-X