读书笔记:第10章 Posix信号量 (5)

《UNIX网络编程:卷2》P197-P198:图10-24、10-25

多个生产者,多个消费者

------------------------------------------------------------------

/*
 * prodcons4.c
 * P197 图10-24 全局变量 
 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <semaphore.h>
#include <pthread.h>

#define NBUFF		10
#define MAXNTHREADS	100
#define min(x,y)	((x) < (y) ? (x) : (y))

void *produce(void *arg);
void *consume(void *arg);

int		nitems;
int		nproducers;
int		nconsumers;

struct {
	int		buff[NBUFF];		// 可以存放NBUFF个条目的缓冲区
	int		nput;				// 下一个待存入值的缓冲区项的下标
	int		nputval;			// 下一个待存入缓冲区的值
	int		nget;				// 任意一个消费者线程待取出的下一个条目的编号
	int		ngetval;			// 存放下一个条目的值
	sem_t	mutex;				// 二值信号量保护生产者和消费者的临界区
	sem_t	nempty;				// 统计共享缓冲区中的空槽位数	
	sem_t	nstored;			// 统计共享缓冲区中已填写的槽位数
} shared;

/*
 * prodcons4.c
 * P197 图10-25 创建多个生产者和多个消费者的main函数
 */
int main(int argc, char *argv[])
{
	int			i, prodcount[MAXNTHREADS], conscount[MAXNTHREADS];
	pthread_t	tid_produce[MAXNTHREADS], tid_consume[MAXNTHREADS];

	if (argc != 4) {
		fprintf(stderr, "usage: prodcons4 <#items> <#producers> <#consumer>\n");
		exit(1);
	}
	nitems = atoi(argv[1]);							// 待存入缓冲区的总条目数
	nproducers = min(atoi(argv[2]), MAXNTHREADS);	// 待创建生产者线程的总数
	nconsumers = min(atoi(argv[3]), MAXNTHREADS);

	/*
	 * 初始化三个信号量
	 */
	if (sem_init(&shared.mutex, 0, 1) == -1) {
		fprintf(stderr, "sem_init error: %s\n", strerror(errno));
		exit(1);
	}
	if (sem_init(&shared.nempty, 0, NBUFF) == -1) {
		fprintf(stderr, "sem_init error: %s\n", strerror(errno));
		exit(1);
	}
	if (sem_init(&shared.nstored, 0, 0) == -1) {
		fprintf(stderr, "sem_init error: %s\n", strerror(errno));
		exit(1);
	}

	// set_concurrency(2);	// 该函数在Linux上无效

	/*
	 * 创建多个生产者线程,多个消费者线程
	 */
	for (i = 0; i < nproducers; i++) {
		prodcount[i] = 0;
		if (pthread_create(&tid_produce[i], NULL, produce, &prodcount[i]) != 0) {
			fprintf(stderr, "pthread_create error: %s\n", strerror(errno));
			exit(1);
		}
	}
	for (i = 0; i < nconsumers; i++) {
		conscount[i] = 0;
		if (pthread_create(&tid_consume[i], NULL, consume, &conscount[i]) != 0) {
			fprintf(stderr, "pthread_create error: %s\n", strerror(errno));
			exit(1);
		}
	}


	/*
	 * 主线程等待所有生产者和消费者终止
	 */
	for (i = 0; i < nproducers; i++) {
		pthread_join(tid_produce[i], NULL);
		printf("producer count[%d] = %d\n", i, prodcount[i]);
	}
	for (i = 0; i < nconsumers; i++) {
		pthread_join(tid_consume[i], NULL);
		printf("consumer count[%d] = %d\n", i , conscount[i]);
	}

	/*
	 * 删除三个信号量
	 */
	sem_destroy(&shared.mutex);
	sem_destroy(&shared.nempty);
	sem_destroy(&shared.nstored);

	exit(0);
}

void *produce(void *arg)
{
	for ( ; ; ) {
		sem_wait(&shared.nempty);			// 生产者等待到缓冲区中有一个条目的空间
		sem_wait(&shared.mutex);			// 二值信号量保护临界区

		if (shared.nput >= nitems) {
			sem_post(&shared.nstored);		// 给消费者解阻塞
			sem_post(&shared.nempty);
			sem_post(&shared.mutex);
			return(NULL);
		}
		shared.buff[shared.nput%NBUFF] = shared.nputval;	// 生产者在缓冲区中存放条目
		shared.nput++;
		shared.nputval++;

		sem_post(&shared.mutex);	
		sem_post(&shared.nstored);			// 增加一个已填写槽位
		*((int*)arg) += 1;
	}
}

/*
 * prodcons4.c
 * P198 图10-26 所有消费者线程都执行的函数
 */
void *consume(void *arg)
{
	int		i;

	for ( ; ; ) {
		sem_wait(&shared.nstored);			// 消费者等待nstroed信号量
		sem_wait(&shared.mutex);			// 二值信号量保护临界区

		if (shared.nget >= nitems) {
			sem_post(&shared.nstored);		// 每个生产者线程终止时,应给nstored加1以让一个消费者线程终止
			sem_post(&shared.mutex);
			return(NULL);
		}
		i = shared.nget % NBUFF;
		if (shared.buff[i] != shared.ngetval)
			printf("error: buff[%d] = %d\n", i, shared.buff[i]);
		shared.nget++;
		shared.ngetval++;

		sem_post(&shared.mutex);
		sem_post(&shared.nempty);			// 增加一个空槽位
		*((int *)arg) += 1;
	}
	return(NULL);
}

你可能感兴趣的:(读书笔记,《UNIX网络编程》)