apue之多线程实现生产者消费者模型
今天,通过一个停车场的例子巩固下所学的生产者消费者的知识。
用一个数据结构来描述下停车场结构:
停车场应该包含停车空间,用数组carpark表示
停车场的车辆容量capatity
停车场现有的车辆数目occupied
下一个进来的车的停车位置nextin
下一个取走车的停车位置nextout
记录停车场进入车辆的总和 cars_in
记录停车场出去车辆的总和cars_out
互斥锁lock,表示该结构的数据被线程互斥的使用
条件变量space,表示停车场是否有空位
条件变量car,描述停车场是否有车
用户线程同步的线程屏障bar
本项目中有两个生产者(网停车场停车)和两个消费者(从停车场取车)和一个监督者(实时打印停车场消息),用5个线程实现
main()函数接受接收命令函的参数,初始化ourpark结构数组:
int main(int argc, char *argv[]) { if (argc != 2) { printf("Usage: %s carparksize\n", argv[0]); exit(1); } cp_t ourpark; initialise(&ourpark, atoi(argv[1])); // 初始化停车场数据结构 pthread_t car_in, car_out, m; // 定义线程变量 pthread_t car_in2, car_out2; pthread_create(&car_in, NULL, car_in_handler, (void *)&ourpark); // 创建往停车场停车线程(生产者1) pthread_create(&car_out, NULL, car_out_handler, (void *)&ourpark); // 创建从停车场取车线程(消费者1) pthread_create(&car_in2, NULL, car_in_handler, (void *)&ourpark); // 创建往停车场停车线程(生产者2) pthread_create(&car_out2, NULL, car_out_handler, (void *)&ourpark); // 创建从停车场取车线程(消费者2) pthread_create(&m, NULL, monitor, (void *)&ourpark); // 创建用于监控停车场状况的线程 // pthread_join 的第二个参数设置为 NULL,表示并不关心线程的返回状态,仅仅等待指定线程(第一个参数)的终止 pthread_join(car_in, NULL); pthread_join(car_out, NULL); pthread_join(car_in2, NULL); pthread_join(car_out2, NULL); pthread_join(m, NULL); exit(0); }
生产者就是网停车场停车的人,函数原型为:static void* car_in_handle(void *carpark_in)
生产者网停车场停车需要读写ourpark数据,由于有2个生产者消费者,所以采用互斥的方式读写,,先获取ourpark的锁:
pthread_mutex_lock(&temp->lock);
释放锁
pthread_mutex_unlock(&temp->lock);
条件变量用来等待一个条件为真,生产者这边需要等待停车场有空位,即等待条件temp->space为真,如果不为真,则需要释放锁,等待消费者的消费,然后重新获取锁
pthread_cond_wait(&temp->space,&temp->lock)
消费者取车之后,同样产生信号,告诉生产者
pthread_cond_signal(&temp->car)
static void* car_in_handler(void *carpark_in) { cp_t *temp; unsigned int seed; temp = (cp_t *)carpark_in; // pthread_barrier_wait 函数表明,线程已完成工作,等待其他线程赶来 pthread_barrier_wait(&temp->bar); while (1) { // 将线程随机挂起一段时间,模拟车辆到来的的随机性 usleep(rand_r(&seed) % ONE_SECOND); pthread_mutex_lock(&temp->lock); // 循环等待直到有停车位 while (temp->occupied == temp->capacity) pthread_cond_wait(&temp->space, &temp->lock); // 插入一个辆车(用随机数标识) temp->carpark[temp->nextin] = rand_r(&seed) % RANGE; // 各变量增量计算 temp->occupied++; temp->nextin++; temp->nextin %= temp->capacity; // 循环计数车辆停车位置 temp->cars_in++; // 可能有的人在等有车可取(线程),这是发送 temp->car 条件变量 pthread_cond_signal(&temp->car); // 释放锁 pthread_mutex_unlock(&temp->lock); } return ((void *)NULL); }
消费者模式与生产者差不多,直接上代码了,
static void* car_out_handler(void *carpark_out) { cp_t *temp; unsigned int seed; temp = (cp_t *)carpark_out; pthread_barrier_wait(&temp->bar); for (; ;) { // 将线程随机挂起一段时间,模拟车辆到来的的随机性 usleep(rand_r(&seed) % ONE_SECOND); // 获取保护停车场结构的锁 pthread_mutex_lock(&temp->lock); /* 获得锁后访问 temp->occupied 变量,此时如果车辆数为0(occupied ==0 ), pthread_cond_wait 进行的操作是忙等,释放锁(&temp->lock)供其它线程使用。 直到 &temp->car 条件改变时再次将锁锁住 */ while (temp->occupied == 0) pthread_cond_wait(&temp->car, &temp->lock); // 增加相应的增量 temp->occupied--; // 现有车辆数目减1 temp->nextout++; temp->nextout %= temp->capacity; temp->cars_out++; // 可能有的人在等有空空车位(线程),这是发送 temp->space 条件变量 pthread_cond_signal(&temp->space); // 释放保护停车场结构的锁 pthread_mutex_unlock(&temp->lock); } return ((void *)NULL); }
停车场监控程序的代码,
static void *monitor(void *carpark_in) { cp_t *temp; temp = (cp_t *)carpark_in; for (; ;) { sleep(PERIOD); // 获取锁 pthread_mutex_lock(&temp->lock); /* 证明锁机制保证线程实现的生产者消费者模型正确的方式是: temp->cars_in - temp->cars_out - temp->occupied == 0,即总的进来的车 == 总的开出去的车 + 停车场现有的车 */ printf("Delta: %d\n", temp->cars_in - temp->cars_out - temp->occupied); printf("Number of cars in carpark: %d\n", temp->occupied); // 释放锁 pthread_mutex_unlock(&temp->lock); } return ((void *)NULL); }