/** threadpool.h */
#include <pthread.h>
struct job {
void *(*callback_function)(void *arg); //线程回调函数
void *arg; //回调函数参数
struct job *next;
};
struct threadpool;
/*
* threadpool_init
* @thread_num: 线程池开启的线程个数
* @queue_max_num: 队列的最大job个数
* return: 成功:线程池地址 失败:NULL
*/
struct threadpool *threadpool_init(int thread_num, int queue_max_num);
/*
* threadpool_add_job
* @pool: 线程池地址
* @callback_function: 回调函数参数
* @arg: 回调函数参数
* return: 成功:0 失败:-1struct threadpool
*/
int threadpool_add_job(struct threadpool *pool, void *(*callback_function)(void *arg), void *arg);
/*
* threadpool_destroy
* @pool: 线程池地址
* return: 成功:0 失败:-1
*/
int threadpool_destroy(struct threadpool *pool);
/** threadpool.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "threadpool.h"
struct threadpool
{
int thread_num; //线程池中开启线程的个数
int queue_max_num; //队列中最大job的个数
struct job *head; //指向job的头指针
struct job *tail; //指向job的尾指针
pthread_t *pthreads; //线程池中所有线程的pthread_t
pthread_mutex_t mutex; //互斥信号量
pthread_cond_t queue_empty; //队列为空的条件变量
pthread_cond_t queue_not_empty; //队列不为空的条件变量
pthread_cond_t queue_not_full; //队列不为满的条件变量
int queue_cur_num; //队列当前的job个数
int queue_close; //队列是否已经关闭
int pool_close; //线程池是否已经关闭
};
/*
threadpool_function --(queue_not_full)--> threadpool_add_job
threadpool_add_job --(queue_not_empty)--> threadpool_function
threadpool_function --(queue_empty)--> threadpool_destroy
*/
void *threadpool_function(void *arg)
{
struct threadpool *pool = (struct threadpool*)arg;
struct job *pjob = NULL;
while (1) {
pthread_mutex_lock(&(pool->mutex));
while ((pool->queue_cur_num == 0) && !pool->pool_close) {
pthread_cond_wait(&(pool->queue_not_empty), &(pool->mutex));
}
if (pool->pool_close) {
pthread_mutex_unlock(&(pool->mutex));
pthread_exit(NULL);
}
pool->queue_cur_num--;
pjob = pool->head;
if (pool->queue_cur_num == 0) {
pool->head = pool->tail = NULL;
} else {
pool->head = pjob->next;
}
if (pool->queue_cur_num == 0) {
pthread_cond_signal(&(pool->queue_empty));
}
if (pool->queue_cur_num < pool->queue_max_num) {
pthread_cond_broadcast(&(pool->queue_not_full));
}
pthread_mutex_unlock(&(pool->mutex));
(*(pjob->callback_function))(pjob->arg);
free(pjob);
pjob = NULL;
}
}
int threadpool_add_job(struct threadpool *pool, void *(*callback_function)(void *arg), void *arg)
{
assert(pool != NULL);
assert(callback_function != NULL);
assert(arg != NULL);
pthread_mutex_lock(&(pool->mutex));
while ((pool->queue_cur_num == pool->queue_max_num) && !(pool->queue_close || pool->pool_close)) {
pthread_cond_wait(&(pool->queue_not_full), &(pool->mutex));
}
if (pool->queue_close || pool->pool_close) {
pthread_mutex_unlock(&(pool->mutex));
return -1;
}
struct job *pjob =(struct job*) malloc(sizeof(struct job));
if (NULL == pjob) {
pthread_mutex_unlock(&(pool->mutex));
return -1;
}
pjob->callback_function = callback_function;
pjob->arg = arg;
pjob->next = NULL;
if (pool->head == NULL) {
pool->head = pool->tail = pjob;
pthread_cond_broadcast(&(pool->queue_not_empty));
} else {
pool->tail->next = pjob;
pool->tail = pjob;
}
pool->queue_cur_num++;
pthread_mutex_unlock(&(pool->mutex));
return 0;
}
struct threadpool *threadpool_init(int thread_num, int queue_max_num)
{
struct threadpool *pool = NULL;
do {
pool = malloc(sizeof(struct threadpool));
if (NULL == pool) {
printf("failed to malloc threadpool!\n");
break;
}
pool->thread_num = thread_num;
pool->queue_max_num = queue_max_num;
pool->queue_cur_num = 0;
pool->head = NULL;
pool->tail = NULL;
if (pthread_mutex_init(&(pool->mutex), NULL)) {
printf("failed to init mutex!\n");
break;
}
if (pthread_cond_init(&(pool->queue_empty), NULL)) {
printf("failed to init queue_empty!\n");
break;
}
if (pthread_cond_init(&(pool->queue_not_empty), NULL)) {
printf("failed to init queue_not_empty!\n");
break;
}
if (pthread_cond_init(&(pool->queue_not_full), NULL)) {
printf("failed to init queue_not_full!\n");
break;
}
pool->pthreads = malloc(sizeof(pthread_t) * thread_num);
if (NULL == pool->pthreads) {
printf("failed to malloc pthreads!\n");
break;
}
pool->queue_close = 0;
pool->pool_close = 0;
int i;
for (i = 0; i < pool->thread_num; ++i) {
pthread_create(&(pool->pthreads[i]), NULL,
threadpool_function, (void *)pool);
}
return pool;
} while (0);
return NULL;
}
int threadpool_destroy(struct threadpool *pool)
{
assert(pool != NULL);
pthread_mutex_lock(&(pool->mutex));
if (pool->queue_close || pool->pool_close) {
pthread_mutex_unlock(&(pool->mutex));
return -1;
}
pool->queue_close = 1;
while (pool->queue_cur_num != 0) {
pthread_cond_wait(&(pool->queue_empty), &(pool->mutex));
}
pool->pool_close = 1;
pthread_mutex_unlock(&(pool->mutex));
pthread_cond_broadcast(&(pool->queue_not_empty));
pthread_cond_broadcast(&(pool->queue_not_full));
int i;
for (i = 0; i < pool->thread_num; ++i) {
pthread_join(pool->pthreads[i], NULL);
}
pthread_mutex_destroy(&(pool->mutex));
pthread_cond_destroy(&(pool->queue_empty));
pthread_cond_destroy(&(pool->queue_not_empty));
pthread_cond_destroy(&(pool->queue_not_full));
free(pool->pthreads);
free(pool);
return 0;
}
/** poolmain.c */
#include <stdio.h>
#include <stdlib.h>
#include "threadpool.h"
#define JOBNUM 2000
void *work(void *arg)
{
printf("threadpool callback fuction : %s.\n", (char *)arg);
free(arg);
usleep(1000 * 100);
return NULL;
}
int main(void)
{
struct threadpool *pool = threadpool_init(10, 20);
int loop = JOBNUM;
while (loop--) {
char *buf = (char *)malloc(sizeof(char) * 32);
snprintf(buf, 32, "%d", JOBNUM - loop);
threadpool_add_job(pool, work, buf);
}
threadpool_destroy(pool);
return 0;
}
pthread_cond_signal restarts one of the threads that are waiting on the condition variable cond.
If no threads are waiting on cond, nothing happens. If several threads are waiting on cond,
exactly one is restarted, but it is not specified which.
pthread_cond_broadcast restarts all the threads that are waiting on the condition variable cond.
Nothing happens if no threads are waiting on cond.
pthread_cond_wait atomically unlocks the mutex (as per pthread_unlock_mutex) and waits for the
condition variable cond to be signaled. The thread execution is suspended and does not consume
any CPU time until the condition variable is signaled. The mutex must be locked by the calling
thread on entrance to pthread_cond_wait. Before returning to the calling thread,
pthread_cond_wait re-acquires mutex (as per pthread_lock_mutex).
Unlocking the mutex and suspending on the condition variable is done atomically. Thus, if all
threads always acquire the mutex before signaling the condition, this guarantees that the condi‐
tion cannot be signaled (and thus ignored) between the time a thread locks the mutex and the time
it waits on the condition variable.
pthread_cond_wait 在进入休眠时会把互斥锁解锁,唤醒时会获取互斥锁
所以在没有add job之前,线程池里所有的线程都是cond wait的状态,等待queue_not_empty