生产者消费者
生产者消费者共享缓冲区,生产者向缓冲区中放数据,消费者从缓冲取中取数据,当缓冲区中被放满时,生产者进程就必须进入挂起状态,直到消费者从缓冲中取走数据时,生产者才能继续向缓冲区中存放数据,同样当缓冲取中没有数据时,消费者进程就必须进入挂起休眠状态,直到生产者向缓冲区中放入数据时,消费者才能被唤醒继续从缓冲区中取走数据。
1.信号量实现生产者消费者:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <semaphore.h>
#define NUM 5
int queue[NUM];
sem_t blank_number, product_number;
void *producer(void *arg) {
int i = 0;
int n = 10;
while (n--) {
sem_wait(&blank_number);
queue[i] = rand() % 1000 + 1;
printf("produce--%d\n", queue[i]);
sem_post(&product_number);
i = (i+1) % NUM;
sleep(rand() % 1);
}
return NULL;
}
void *consumer(void *arg) {
int i = 0;
int n = 10;
while (n--) {
sem_wait(&product_number);
printf("consumer--%d\n", queue[i]);
queue[i] = 0;
sem_post(&blank_number);
i = (i + 1) % NUM;
sleep(rand() % 3);
}
return NULL;
}
int main() {
pthread_t pid, cid;
sem_init(&blank_number, 0, NUM);
sem_init(&product_number, 0, 0);
pthread_create(&pid, NULL, producer, NULL);
pthread_create(&cid, NULL, consumer, NULL);
pthread_join(pid, NULL);
pthread_join(cid, NULL);
sem_destroy(&blank_number);
sem_destroy(&product_number);
return 0;
}
2.条件变量实现生产者消费者:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#define CAPACITY 10
void err_thread(int ret, char *str) {
if (ret != 0) {
fprintf(stderr, "%s:%s\n", str, strerror(ret));
pthread_exit(NULL);
}
}
struct msg {
int num;
struct msg *next;
};
struct msg *head;
int size;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t has_data = PTHREAD_COND_INITIALIZER;
void *producer(void *arg) {
struct msg *mp = malloc(sizeof(struct msg));
int n = 10;
while (n--) {
mp->num = rand() % 1000 + 1;
while (size >= CAPACITY) { // 缓冲区满了
printf("size >= CAPACITY缓冲区满了\n");
sleep(rand() % 3);
}
pthread_mutex_lock(&mutex);
mp->next = head;
head = mp;
size++;
pthread_mutex_unlock(&mutex);
printf("--produce id =%lu produce %d size=%d\n", pthread_self(), mp->num, size);
pthread_cond_signal(&has_data); // 将阻塞在该条件变量上的一个线程唤醒
sleep(rand() % 3);
}
return NULL;
}
void *consumer(void *arg) {
struct msg *mp;
pthread_mutex_lock(&mutex); // 加锁
int n = 10;
while (n--) {
while (head == NULL) { // 注意要用while
pthread_cond_wait(&has_data, &mutex); // 阻塞等待条件变量, 判断条件是否满足
}
mp = head;
head = mp->next;
size--;
pthread_mutex_unlock(&mutex);
printf("--consume id: %lu, :%d size=%d\n", pthread_self(), mp->num, size);
sleep(rand() % 3);
}
return NULL;
}
int main() {
pthread_t pid[3], cid[3];
int ret;
srand(time(NULL));
for (int i = 0; i < 10; ++i) {
ret = pthread_create(&pid[i], NULL, producer, NULL);
if (ret != 0) {
err_thread(ret, "pthread_create producer error");
}
}
for (int i = 0; i < 3; ++i) {
ret = pthread_create(&cid[i], NULL, consumer, NULL);
if (ret != 0) {
err_thread(ret, "pthread_create consumer error");
}
}
for (int i = 0; i < 3; ++i) {
pthread_join(pid[i], NULL);
pthread_join(cid[i], NULL);
}
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&has_data);
return 0;
}
哲学家问题
有五个哲学家绕着圆桌坐,每个哲学家面前有一盘面,两人之间有一支筷子,这样每个哲学家左右各有一支筷子。哲学家有2个状态,思考或者拿起筷子吃饭。如果哲学家拿到一只筷子,不能吃饭,直到拿到2只才能吃饭,并且一次只能拿起身边的一支筷子。一旦拿起便不会放下筷子直到把饭吃完,此时才把这双筷子放回原处。如果,很不幸地,每个哲学家拿起他或她左边的筷子,那么就没有人可以吃到饭了。
哲学家进餐问题是一个多线程运用的经典例子,涉及到线程同步/互斥,临界区访问问题以及死锁问题。
可能导致的死锁问题:
如果5个哲学家都去拿他左边的筷子, 再拿右边的筷子, 会导致每个人都在等待筷子, 导致死锁问题.
解决办法1:
最多允许4个哲学家吃饭, 第5个人想要拿筷子, 要阻塞直到有1个哲学家吃完, 释放筷子.
那怎么让第5个线程想要拿筷子前, 阻塞住?
用信号量机制, 信号量初始化为4, 每个线程拿筷子前调用sem_wait(), 吃完后, 调用sem_post(), 这样在第5个线程要拿筷子时, 就会阻塞, 直到有一个线程吃完后才会解除阻塞, 从而避免了死锁.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define NUM 4
/**
* 最多允许4个哲学家吃饭, 第5个想吃要阻塞直到有1个哲学家吃完
*/
pthread_mutex_t chops[5];
sem_t max_philosopher_num;
void think(int i) {
printf("I am thinking %d\n", i);
sleep(rand() % 3);
}
void hungry(int i) {
printf("I am hungry %d\n", i);
sleep(rand() % 3);
}
void eat(int i) {
printf("I am eating %d\n", i);
sleep(rand() % 3);
}
void *philosopher(void *arg) {
int i = (int) arg;
think(i);
hungry(i);
sem_wait(&max_philosopher_num);
pthread_mutex_lock(&chops[i]);
pthread_mutex_lock(&chops[(i + 1) % 5]);
eat(i);
pthread_mutex_unlock(&chops[i]);
pthread_mutex_unlock(&chops[(i + 1) % 5]);
sem_post(&max_philosopher_num);
return NULL;
}
int main() {
pthread_t tid[5];
srand(time(NULL));
for (int i = 0; i < 5; ++i) {
pthread_mutex_init(&chops[i], NULL);
}
sem_init(&max_philosopher_num, 0, NUM);
for (int i = 0; i < 5; ++i) {
pthread_create(&tid[i], NULL, philosopher, (void *) i);
}
for (int i = 0; i < 5; ++i) {
pthread_join(tid[i], NULL);
}
for (int i = 0; i < 5; ++i) {
pthread_mutex_destroy(&chops[i]);
}
sem_destroy(&max_philosopher_num);
return 0;
}
解决办法2:
如果想给某个哲学家筷子,就将他需要的所有筷子都给他,然后让他进餐,否则就一个都不给他。从而避免了死锁。
怎么实现呢?
搞一个全局互斥量, 然后在某个线程拿筷子前, 先去拿到这个全局互斥量, 然后在拿他所需要的2个筷子, 因为别的线程没有抢到这个互斥量, 所以不能拿筷子, 而在这个互斥量这里阻塞.
所以这个拿到互斥量的线程可以拿到所有的筷子进餐, 在进餐结束后释放这个互斥量即可.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
/**
* 如果想给某个哲学家筷子,就将他需要的所有资源都给他,然后让他进餐,否则就一个都不给他。
*/
pthread_mutex_t mutex, chops[5];
void think(int i) {
printf("I am thinking %d\n", i);
sleep(rand() % 3);
}
void hungry(int i) {
printf("I am hungry %d\n", i);
sleep(rand() % 3);
}
void eat(int i) {
printf("I am eating %d\n", i);
sleep(rand() % 3);
}
void *philosopher(void *arg) {
int i = (int)arg;
think(i);
hungry(i);
pthread_mutex_lock(&mutex); // 阻止其他线程拿筷子
pthread_mutex_lock(&chops[i]);
pthread_mutex_lock(&chops[(i + 1) % 5]);
pthread_mutex_unlock(&mutex);
eat(i);
pthread_mutex_unlock(&chops[i]);
pthread_mutex_unlock(&chops[(i + 1) % 5]);
return NULL;
}
int main() {
pthread_t tid[5];
srand(time(NULL));
pthread_mutex_init(&mutex, NULL);
for (int i = 0; i < 5; ++i) {
pthread_mutex_init(&chops[i], NULL);
}
for (int i = 0; i < 5; ++i) {
pthread_create(&tid[i], NULL, philosopher, (void *)i);
}
for (int i = 0; i < 5; ++i) {
pthread_join(tid[i], NULL);
}
pthread_mutex_destroy(&mutex);
for (int i = 0; i < 5; ++i) {
pthread_mutex_destroy(&chops[i]);
}
return 0;
}
解决办法3:
让奇数号哲学家先去拿他左边的筷子, 然后再拿他右边的筷子, 偶数号哲学家相反, 这样可以保证至少有一个哲学家可以拿到2个筷子进餐然后再释放2个筷子.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
pthread_mutex_t chops[5];
void think(int i) {
printf("I am thinking %d\n", i);
sleep(rand() % 3);
}
void hungry(int i) {
printf("I am hungry %d\n", i);
sleep(rand() % 3);
}
void eat(int i) {
printf("I am eating %d\n", i);
sleep(rand() % 3);
}
void *philosopher(void *arg) {
int i = (int)arg;
think(i);
hungry(i);
if (i % 2 == 0) {
pthread_mutex_lock(&chops[(i + 1) % 5]);
pthread_mutex_lock(&chops[i]);
eat(i);
pthread_mutex_unlock(&chops[i]);
pthread_mutex_unlock(&chops[(i + 1) % 5]);
} else {
pthread_mutex_lock(&chops[i]);
pthread_mutex_lock(&chops[(i + 1) % 5]);
eat(i);
pthread_mutex_unlock(&chops[i]);
pthread_mutex_unlock(&chops[(i + 1) % 5]);
}
return NULL;
}
int main() {
pthread_t tid[5];
srand(time(NULL));
for (int i = 0; i < 5; ++i) {
pthread_mutex_init(&chops[i], NULL);
}
for (int i = 0; i < 5; ++i) {
pthread_create(&tid[i], NULL, philosopher, (void *)i);
}
for (int i = 0; i < 5; ++i) {
pthread_join(tid[i], NULL);
}
for (int i = 0; i < 5; ++i) {
pthread_mutex_destroy(&chops[i]);
}
return 0;
}
线程池
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <signal.h>
#include <pthread.h>
#include <sys/types.h>
//#include "threadpool.h"
#define DEFAULT_TIME 10 /*10s检测一次*/
#define MIN_WAIT_TASK_NUM 10 /*如果queue_size > MIN_WAIT_TASK_NUM 添加新的线程到线程池*/
#define DEFAULT_THREAD_VARY 10 /*每次创建和销毁线程的个数*/
#define true 1
#define false 0
typedef struct {
void *(*function)(void *); // 函数指针, 回调函数
void *arg; // 上面函数的参数
} threadpool_task_t; // 各子线程任务结构体
// 描述线程池相关信息
typedef struct threadpoll_t {
pthread_mutex_t lock; /* 用于锁住本结构体 */
pthread_mutex_t thread_counter; /* 记录忙状态线程个数de琐 -- busy_thr_num */
pthread_cond_t queue_not_full; /* 当任务队列满时,添加任务的线程阻塞,等待此条件变量 */
pthread_cond_t queue_not_empty; /* 任务队列里不为空时,通知等待任务的线程 */
pthread_t *threads; // 存放线程池中每个线程的tid
pthread_t adjust_tid; // 存管理线程tid
threadpool_task_t *task_queue; // 任务队列(数组首地址)
int min_thr_num;
int max_thr_num;
int live_thr_num;
int busy_thr_num;
int wait_exit_thr_num; // 要销毁的线程个数
int queue_front;
int queue_rear;
int queue_size; // 队列中实际任务数
int queue_max_size; // 队列容量
int shutdown;
} threadpool_t;
void *threadpool_thread(void *threadpool);
void *adjust_thread(void *threadpool);
int is_thread_alive(pthread_t tid);
int threadpool_free(threadpool_t *pool);
threadpool_t *threadpoll_create(int min_thr_num, int max_thr_num, int queue_max_size) {
int i;
threadpool_t *pool = NULL;
do {
if ((pool = (threadpool_t *) malloc(sizeof(threadpool_t))) == NULL) {
printf("malloc threadpoll fail");
break;
}
pool->min_thr_num = min_thr_num;
pool->max_thr_num = max_thr_num;
pool->busy_thr_num = 0;
pool->live_thr_num = min_thr_num;
pool->wait_exit_thr_num = 0;
pool->queue_size = 0;
pool->queue_max_size = queue_max_size;
pool->queue_front = 0;
pool->queue_rear = 0;
pool->shutdown = false;
pool->threads = (pthread_t *) malloc(sizeof(pthread_t) * max_thr_num);
if (pool->threads == NULL) {
printf("malloc threads fail");
break;
}
memset(pool->threads, 0, sizeof(pthread_t) * max_thr_num);
/* 给 任务队列 开辟空间 */
pool->task_queue = (threadpool_task_t *) malloc(sizeof(threadpool_task_t) * queue_max_size);
if (pool->task_queue == NULL) {
printf("malloc task_queue fail");
break;
}
/* 初始化互斥琐、条件变量 */
if (pthread_mutex_init(&(pool->lock), NULL) != 0
|| pthread_mutex_init(&(pool->thread_counter), NULL) != 0
|| pthread_cond_init(&(pool->queue_not_empty), NULL) != 0
|| pthread_cond_init(&(pool->queue_not_full), NULL) != 0) {
printf("init the lock or cond fail");
break;
}
/* 启动 min_thr_num 个 work thread */
for (i = 0; i < min_thr_num; i++) {
pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void *) pool); /*pool指向当前线程池*/
printf("start thread 0x%x...\n", (unsigned int) pool->threads[i]);
}
pthread_create(&(pool->adjust_tid), NULL, adjust_thread, (void *) pool); /* 创建管理者线程 */
return pool;
} while (0);
threadpool_free(pool); /* 前面代码调用失败时,释放poll存储空间 */
return NULL;
}
void *threadpool_thread(void *threadpool) {
threadpool_t *pool = (threadpool_t *) threadpool;
threadpool_task_t task;
while (true) {
/* Lock must be taken to wait on conditional variable */
/*刚创建出线程,等待任务队列里有任务,否则阻塞等待任务队列里有任务后再唤醒接收任务*/
pthread_mutex_lock(&(pool->lock));
/*queue_size == 0 说明没有任务,调 wait 阻塞在条件变量上, 若有任务,跳过该while*/
while ((pool->queue_size == 0) && (!pool->shutdown)) {
printf("thread 0x%x is waiting\n", (unsigned int) pthread_self());
pthread_cond_wait(&(pool->queue_not_empty), &(pool->lock));
/*清除指定数目的空闲线程,如果要结束的线程个数大于0,结束线程*/
if (pool->wait_exit_thr_num > 0) {
pool->wait_exit_thr_num--;
/*如果线程池里线程个数大于最小值时可以结束当前线程*/
if (pool->live_thr_num > pool->min_thr_num) {
printf("thread 0x%x is exiting\n", (unsigned int) pthread_self());
pool->live_thr_num--;
pthread_mutex_unlock(&(pool->lock));
pthread_exit(NULL);
}
}
}
/*如果指定了true,要关闭线程池里的每个线程,自行退出处理---销毁线程池*/
if (pool->shutdown) {
pthread_mutex_unlock(&(pool->lock));
printf("thread 0x%x is exiting\n", (unsigned int) pthread_self());
pthread_detach(pthread_self());
pthread_exit(NULL); /* 线程自行结束 */
}
/*从任务队列里获取任务, 是一个出队操作*/
task.function = pool->task_queue[pool->queue_front].function;
task.arg = pool->task_queue[pool->queue_front].arg;
pool->queue_front = (pool->queue_front + 1) % pool->queue_max_size; /* 出队,模拟环形队列 */
pool->queue_size--;
/*通知可以有新的任务添加进来*/
pthread_cond_signal(&(pool->queue_not_full));
/*任务取出后,立即将 线程池琐 释放*/
pthread_mutex_unlock(&(pool->lock));
/*执行任务*/
printf("thread 0x%x start working\n", (unsigned int) pthread_self());
pthread_mutex_lock(&(pool->thread_counter)); /*忙状态线程数变量琐*/
pool->busy_thr_num++; /*忙状态线程数+1*/
pthread_mutex_unlock(&(pool->thread_counter));
(*(task.function))(task.arg); /*执行回调函数任务*/
//task.function(task.arg); /*执行回调函数任务*/
/*任务结束处理*/
printf("thread 0x%x end working\n", (unsigned int) pthread_self());
pthread_mutex_lock(&(pool->thread_counter));
pool->busy_thr_num--; /*处理掉一个任务,忙状态数线程数-1*/
pthread_mutex_unlock(&(pool->thread_counter));
}
pthread_exit(NULL);
}
/* 管理线程 */
void *adjust_thread(void *threadpool) {
int i;
threadpool_t *pool = (threadpool_t *) threadpool;
while (!pool->shutdown) {
sleep(DEFAULT_TIME);
pthread_mutex_lock(&pool->lock);
int queue_size = pool->queue_size;
int live_thr_num = pool->live_thr_num;
pthread_mutex_unlock(&pool->lock);
pthread_mutex_lock(&pool->thread_counter);
int busy_thr_num = pool->busy_thr_num;
pthread_mutex_unlock(&pool->thread_counter);
/* 创建新线程 算法: 任务数大于最小线程池个数, 且存活的线程数少于最大线程个数时 如:30>=10 && 40<100*/
if (queue_size >= MIN_WAIT_TASK_NUM && live_thr_num < pool->max_thr_num) {
pthread_mutex_lock(&(pool->lock));
int add = 0;
/*一次增加 DEFAULT_THREAD 个线程*/
for (i = 0; i < pool->max_thr_num && add < DEFAULT_THREAD_VARY
&& pool->live_thr_num < pool->max_thr_num; i++) {
if (pool->threads[i] == 0 || !is_thread_alive(pool->threads[i])) {
pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void *) pool);
add++;
pool->live_thr_num++;
}
}
pthread_mutex_unlock(&(pool->lock));
}
/* 销毁多余的空闲线程 算法:忙线程X2 小于 存活的线程数 且 存活的线程数 大于 最小线程数时*/
if ((busy_thr_num * 2) < live_thr_num && live_thr_num > pool->min_thr_num) {
/* 一次销毁DEFAULT_THREAD个线程, 随机10个即可 */
pthread_mutex_lock(&(pool->lock));
pool->wait_exit_thr_num = DEFAULT_THREAD_VARY; /* 要销毁的线程数 设置为10 */
pthread_mutex_unlock(&(pool->lock));
for (i = 0; i < DEFAULT_THREAD_VARY; i++) {
/* 通知处在空闲状态的线程, 他们会自行终止*/
pthread_cond_signal(&(pool->queue_not_empty));
}
}
}
return NULL;
}
int threadpool_destroy(threadpool_t *pool) {
int i;
if (pool == NULL) {
return -1;
}
pool->shutdown = true;
/*先销毁管理线程*/
pthread_join(pool->adjust_tid, NULL);
for (i = 0; i < pool->live_thr_num; i++) {
/*通知所有的空闲线程*/
pthread_cond_broadcast(&(pool->queue_not_empty));
}
for (i = 0; i < pool->live_thr_num; i++) {
pthread_join(pool->threads[i], NULL);
}
threadpool_free(pool);
return 0;
}
int threadpool_free(threadpool_t *pool) {
if (pool == NULL) {
return -1;
}
if (pool->task_queue) {
free(pool->task_queue);
}
if (pool->threads) {
free(pool->threads);
pthread_mutex_lock(&(pool->lock));
pthread_mutex_destroy(&(pool->lock));
pthread_mutex_lock(&(pool->thread_counter));
pthread_mutex_destroy(&(pool->thread_counter));
pthread_cond_destroy(&(pool->queue_not_empty));
pthread_cond_destroy(&(pool->queue_not_full));
}
free(pool);
pool = NULL;
return 0;
}
int threadpool_all_threadnum(threadpool_t *pool) {
int all_threadnum = -1; // 总线程数
pthread_mutex_lock(&(pool->lock));
all_threadnum = pool->live_thr_num; // 存活线程数
pthread_mutex_unlock(&(pool->lock));
return all_threadnum;
}
int threadpool_busy_threadnum(threadpool_t *pool) {
int busy_threadnum = -1; // 忙线程数
pthread_mutex_lock(&(pool->thread_counter));
busy_threadnum = pool->busy_thr_num;
pthread_mutex_unlock(&(pool->thread_counter));
return busy_threadnum;
}
//int is_thread_alive(pthread_t tid)
//{
// int kill_rc = pthread_kill(tid, 0); //发0号信号,测试线程是否存活
// if (kill_rc == ESRCH) {
// return false;
// }
// return true;
//}
/* 线程池中的线程,模拟处理业务 */
void *process(void *arg) {
printf("thread 0x%x working on task %d\n ", (unsigned int) pthread_self(), (int) arg);
sleep(1); //模拟 小---大写
printf("task %d is end\n", (int) arg);
return NULL;
}
/* 向线程池中 添加一个任务 */
//threadpool_add(thp, process, (void*)&num[i]); /* 向线程池中添加任务 process: 小写---->大写*/
int threadpool_add(threadpool_t *pool, void *(*function)(void *arg), void *arg) {
pthread_mutex_lock(&(pool->lock));
/* ==为真,队列已经满, 调wait阻塞 */
while ((pool->queue_size == pool->queue_max_size) && (!pool->shutdown)) {
pthread_cond_wait(&(pool->queue_not_full), &(pool->lock));
}
if (pool->shutdown) {
pthread_cond_broadcast(&(pool->queue_not_empty));
pthread_mutex_unlock(&(pool->lock));
return 0;
}
/* 清空 工作线程 调用的回调函数 的参数arg */
if (pool->task_queue[pool->queue_rear].arg != NULL) {
pool->task_queue[pool->queue_rear].arg = NULL;
}
/*添加任务到任务队列里*/
pool->task_queue[pool->queue_rear].function = function; // 设置回调函数为process
pool->task_queue[pool->queue_rear].arg = arg; // 回调函数对应的参数
pool->queue_rear = (pool->queue_rear + 1) % pool->queue_max_size; /* 队尾指针移动, 模拟环形 */
pool->queue_size++; // 队列中的实际任务数+1
/*添加完任务后,队列不为空,唤醒线程池中 等待处理任务的线程*/
pthread_cond_signal(&(pool->queue_not_empty));
pthread_mutex_unlock(&(pool->lock));
return 0;
}
int main() {
threadpool_t *thp = threadpoll_create(3, 100, 100);
printf("poll inited");
int num[20], i;
for (i = 0; i < 20; ++i) {
num[i] = i;
printf("add task %d\n", i);
/*int threadpool_add(threadpool_t *pool, void*(*function)(void *arg), void *arg) */
threadpool_add(thp, process, (void *) &num[i]); /* 向线程池中添加任务 */
}
sleep(10);
threadpool_destroy(thp);
}