消費者/生產者模型是多線程編程開發的常用模型,該模型通過平衡生產者線程和消費者線程的工作能力來提高程式整體的資料處理能力。
一般來說,我們的資源集區是有限的,訪問資源需要加鎖,訪問完以後需要解鎖,生產者需要在資源集區未滿的情況下才能生產產品,消費者需要在資源集區不空的情況下才能消費產品。且在設計時需要注意死結問題。
下面採用semaphore,資源集區為環形緩衝區,來實現生產者消費者模型:
#include <unistd.h>#include <errno.h>#include <pthread.h>#include <semaphore.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#define ERROR(func, no) { \ fprintf(stderr, "%s: %s\n", func, strerror(no)); \ exit(EXIT_FAILURE); \ }#define DEFAULT_CONSUMER_CNT 1#define DEFAULT_PRODUCER_CNT 3#define DEFAULT_BUFFER_SIZE 10static size_t in; // producer's current posstatic size_t out; // consumer's current posstatic size_t consumer_id; // current product id for consumer static size_t producer_id; // current product id for producerstatic size_t consumer_cnt; // count of consumersstatic size_t producer_cnt; // count of producersstatic size_t buff_size; // resource buffer sizestatic int* g_buffer = NULL; // pointer to resource bufferstatic pthread_t* g_thread = NULL; // pointer to thread IDsstatic pthread_mutex_t g_mutex; //mutexstatic sem_t* g_sem_empty_ptr = NULL; // semaphore for consumerstatic sem_t* g_sem_full_ptr = NULL; // semaphore for producervoid* consume(void* arg) { int id = *(int*)arg; free(arg); while(1) { printf("Consumer[%d] waits buffer not empty\n", id); sem_wait(g_sem_empty_ptr); pthread_mutex_lock(&g_mutex); int i, flag; for(i=0; i<buff_size; ++i) { printf("%2d:", i); if(0 == g_buffer[i]) { printf(" nil"); if(out == i) { out = (out+1)%buff_size; printf("\n"); flag = 1; continue; } } else { printf(" %d", g_buffer[i]); } if(out == i) { printf("\t<-- consume[%d]", id); flag = 0; } printf("\n"); } if(0 == flag) { consumer_id = g_buffer[out]; printf("Consumer[%d] begins consuming product %lu\n", id, consumer_id); g_buffer[out] = 0; out = (out+1)%buff_size; printf("Consumer[%d] ends consuming product %lu\n", id, consumer_id); pthread_mutex_unlock(&g_mutex); sem_post(g_sem_full_ptr); } else { printf("*************No product to consume\n"); pthread_mutex_unlock(&g_mutex); } sleep(1); } return NULL;}void* produce(void* arg) { int id = *(int*)arg; free(arg); while(1) { printf("Producer[%d] waits buffer not full\n", id); sem_wait(g_sem_full_ptr); pthread_mutex_lock(&g_mutex); int i, flag; for(i=0; i<buff_size; ++i) { printf("%2d:", i); if(0 == g_buffer[i]) { printf(" nil"); } else { printf(" %d", g_buffer[i]); if(in == i) { in = (in+1)%buff_size; printf("\n"); flag = 1; continue; } } if(in == i) { printf("\t<-- produce[%d]", id); flag = 0; } printf("\n"); } if(0 == flag) { g_buffer[in] = ++producer_id; printf("Producer[%d] begins to produce product %lu\n", id, producer_id); in = (in+1)%buff_size; printf("Producer[%d] ends to produce product %lu\n", id, producer_id); pthread_mutex_unlock(&g_mutex); sem_post(g_sem_empty_ptr); } else { printf("*************No buffer to product\n"); pthread_mutex_unlock(&g_mutex); } sleep(1); } return NULL;}int main(int argc, char** argv){ consumer_cnt = DEFAULT_CONSUMER_CNT; producer_cnt = DEFAULT_PRODUCER_CNT; buff_size = DEFAULT_BUFFER_SIZE; char* prog = argv[0]; int ch; while ((ch = getopt(argc, argv, "b:c:p:")) != -1) { switch (ch) { case 'b': buff_size = atoi(optarg); //printf("-b option: %s\n", optarg); case 'c': consumer_cnt = atoi(optarg); //printf("-c option: %s\n", optarg); break; case 'p': producer_cnt = atoi(optarg); //printf("-p option: %s\n", optarg); break; case '?': default: printf("Usage: %s [-b buffersize] [-p producer_cnt] [-c consumer_cnt]\n" "\tdefault buffersize=10, producer_cnt=3, consumer_cnt=1\n", prog); exit(EXIT_FAILURE); } } g_buffer = (int*)malloc(buff_size*sizeof(int)); g_thread = (pthread_t*)malloc((consumer_cnt+producer_cnt)*sizeof(pthread_t)); memset(g_buffer, 0, buff_size*sizeof(int)); memset(g_thread, 0, (consumer_cnt+producer_cnt)*sizeof(pthread_t)); g_sem_full_ptr = (sem_t*)malloc(sizeof(sem_t)); g_sem_empty_ptr = (sem_t*)malloc(sizeof(sem_t)); g_sem_full_ptr = sem_open("sem_producer", O_CREAT, 0, buff_size); //set semaphore full initially to the buffer length g_sem_empty_ptr = sem_open("sem_consumer", O_CREAT, 0, 0); //set semaphore full initially to 0 pthread_mutex_init(&g_mutex, NULL); int i, ret; for(i=0; i<consumer_cnt; ++i) { void *arg = malloc(sizeof(int)); memcpy(arg, &i, sizeof(int)); ret = pthread_create(&g_thread[i], NULL, consume, arg); if(ret) { ERROR("pthread_create", ret); } } for(i=0; i<producer_cnt; ++i) { void *arg = malloc(sizeof(int)); memcpy(arg, &i, sizeof(int)); ret = pthread_create(&g_thread[i+consumer_cnt], NULL, produce, arg); if(ret) { ERROR("pthread_create", ret); } } for(i=0; i<consumer_cnt+producer_cnt; ++i) { ret = pthread_join(g_thread[i], NULL); if(ret) { ERROR("pthread_join", ret); } } sem_close(g_sem_full_ptr); sem_close(g_sem_empty_ptr); sem_unlink("sem_consumer"); sem_unlink("sem_producer"); pthread_mutex_destroy(&g_mutex); free(g_buffer); free(g_thread); free(g_sem_full_ptr); free(g_sem_empty_ptr); exit(EXIT_SUCCESS);