1. 条件变量实现
生产者、消费者各1个。生产者往链表插入结点,消费者从链表删除结点。
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
pthread_cond_t cond;
pthread_mutex_t mutex;
typedef struct _node_t { // 链表结点。
int data;
struct _node_t* next;
}node_t;
node_t* head = NULL;
void* producer(void* arg) { // 生产者
node_t* new = NULL;
while (1) { // 循环生产
pthread_mutex_lock(&mutex);
new = malloc(sizeof(node_t)); // 分配结点空间
if (NULL == new) {
printf("malloc node_t failed...\n");
break;
}
memset(new, 0, sizeof(node_t));
new->data = random() % 100 + 1;
new->next = NULL;
// 头插法
new->next = head;
head = new;
printf("生产:%d\n", head->data);
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond); // 通知可以消费了
sleep(1);
}
return NULL;
}
void* consumer(void* arg) { // 消费者
node_t* tmp = NULL;
while (1) { // 循环消费
pthread_mutex_lock(&mutex);
if (NULL == head) {
// 等待有链表有结点
pthread_cond_wait(&cond, &mutex);
} else {
// 消费第一个结点
tmp = head;
head = head->next;
printf("消费:%d\n", tmp->data);
free(tmp);
}
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main(int argc, const char* argv[]) {
pthread_t tid1, tid2;
pthread_cond_init(&cond, NULL);
pthread_mutex_init(&mutex, NULL);
pthread_create(&tid1, NULL, producer, NULL); // 生产者线程
pthread_create(&tid2, NULL, consumer, NULL); // 消费者线程
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return 0;
}
运行结果:
注意
上述代码,若将生产者线程中的printf打印放在释放mutex之后,如下图:
则会出现段错误,如下图:
原因:将生产者线程中的printf打印放在释放mutex之后,会出现如下情形:
生产者:lock → 插入1个结点 → unlock → 通知消费者;因为通知了消费者,导致后续的printf打印语句还未执行到,消费者就把刚生产出来的结点消费free掉了,此时生产者想printf访问一个已经被释放的结点,就出现段错误。 因此这里要将printf放在mutex范围内。
2. 信号量实现
生产者2个,消费者4个。生产者往链表插入结点,消费者从链表删除结点。链表最多运行有8个结点。
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
#include<semaphore.h>
sem_t sem_containers; // 可用容器个数
sem_t sem_goods; // 可消费资源个数
pthread_mutex_t mutex;
typedef struct _node_t { // 链表结点。
int data;
struct _node_t* next;
}node_t;
node_t* head = NULL;
void* producer(void* arg) { // 生产者
int index = (int)(long)arg;
node_t* new = NULL;
while (1) { // 循环生产
sem_wait(&sem_containers); // 生产一个,可用容器个数-1
pthread_mutex_lock(&mutex);
new = malloc(sizeof(node_t)); // 分配结点空间
if (NULL == new) {
printf("malloc node_t failed...\n");
break;
}
memset(new, 0, sizeof(node_t));
new->data = random() % 100 + 1;
new->next = NULL;
// 头插法
new->next = head;
head = new;
printf("生产%d生产了:%d\n", index, head->data);
pthread_mutex_unlock(&mutex);
sem_post(&sem_goods); // 生产完了,可消费资源个数+1
}
return NULL;
}
void* consumer(void* arg) { // 消费者
int index = (int)(long)arg;
node_t* tmp = NULL;
while (1) { // 循环消费
sem_wait(&sem_goods); // 消费一个,可消费资源个数-1
pthread_mutex_lock(&mutex);
// 消费第一个结点
tmp = head;
head = head->next;
printf("消费者%d消费了:%d\n", index, tmp->data);
free(tmp);
pthread_mutex_unlock(&mutex);
sem_post(&sem_containers);// 消费完了,可用容器个数+1
sleep(1);
}
return NULL;
}
int main(int argc, const char* argv[]) {
pthread_t tid[6];
sem_init(&sem_containers, 0, 8); // 初始可用容器个数为8
sem_init(&sem_goods, 0, 0); // 初始可消费资源个数为0
pthread_mutex_init(&mutex, NULL);
for (int i = 0; i < 6; i++) {
if (i < 2) {
pthread_create(&tid[i], NULL, producer, (void*)(long)i); // 2个生产者线程
} else {
pthread_create(&tid[i], NULL, consumer, (void*)(long)i); // 4个消费者线程
}
}
for (int i = 0; i < 6; i++) {
pthread_join(tid[i], NULL);
}
sem_destroy(&sem_containers);
sem_destroy(&sem_goods);
pthread_mutex_destroy(&mutex);
return 0;
}
运行结果: