从运行结果中的进程号可以看出总共有5个线程在工作,任务分为两次被执行,前五个running是一次,后五个running是一次
线程池就是有一堆已经创建好了的线程,初始它们都处于空闲等待状态,当有新的任务需要处理的时候,就从这个池子里面取一个空闲等待的线程来处理该任务,当处理完成了就再次把该线程放回池中,以供后面的任务使用。当池子里的线程全都处理忙碌状态时,线程池中没有可用的空闲等待线程,此时,根据需要选择创建一个新的线程并置入池中,或者通知任务线程池忙,稍后再试。
为什么要用线程池,线程的创建和销毁比之进程的创建和销毁是轻量级的,但是当一个应用需要频繁的创建和销毁线程,而任务执行的时间又非常短,这样线程创建和销毁的带来的开销就不容忽视,这是就可以使用线程池了(若线程创建和销毁时间相比任务执行时间可以忽略不计,则不必要使用线程池了)。线程池的好处就在于线程复用,一个任务处理完成后,当前线程可以直接处理下一个任务,而不是销毁后再创建,非常适用于连续产生大量并发任务的场合。
线程池工作原理(线程池负责线程的创建,销毁和任务处理参数传递、唤醒和等待):
(1)创建若干线程,置入线程池
(2)任务达到时,从线程池取空闲线程
(3)取得了空闲线程,立即进行任务处理
(4)否则新建一个线程,并置入线程池,执行(3)
(5)如果创建失败或者线程池已满,根据设计策略选择返回错误或将任务置入处理队列,等待处理
(6)销毁线程池
二、详解
1、简单的线程池代码一
(1)threadpool.h:
#ifndef _THREADPOOL_H_
#define _THREADPOOL_H_
#include
#include
#include
#include
#define THWK_F_CLEAN 1 /* 设置此标志着threadpool正在进行清理操作,此时线程退出。 */
#define THWK_F_RUNNING 2 /* 设置这个标志主要是为了避免一个race condition,后述。 */
#define EINVAL 1
#define EBUSY 2
struct thread_worker_arg {
void (*action)(void*); /* user programmer指定的实际函数 */
void *what; /* action的参数 */
};
struct thread_worker {
pthread_t id; /* just as its name */
struct thread_worker_arg arg; /* 用于给sleepy_wrapper()传送参数,后述。 */
pthread_mutex_t lock; /* 用于实现线程池内空闲线程的休眠,它实际上并不保护什么临界区。 */
struct thread_worker *next; /* 用于链表线程池内的其他线程 */
unsigned long long delay; /* 未用,计划用于测量调度延迟。 */
unsigned long flags; /* 标志,后述。 */
};
struct thread_pool {
pthread_mutex_t lock; /* 用于同步对于thread_pool自身的访问操作 */
struct thread_worker *first; /* 所有线程链接于此 */
int total; /* 总线程数 */
int current_nr; /* 池内空闲线程数 */
};
struct thread_pool* thread_pool_create(int nr_to_create, pthread_attr_t *attr);
int thread_pool_lend(struct thread_pool *pool, void (*action)(void*), void* what, struct thread_worker **worker);
int thread_pool_giveback(struct thread_pool *pool, struct thread_worker *worker);
int thread_pool_clean(struct thread_pool *pool);
int thread_pool_is_running(struct thread_worker *worker);
void thread_pool_activate(struct thread_worker *worker);
#endif
(2)threadpool.c:
#include "threadpool.h" /* #include了所有必要的系统头文件 */
/* 未用,计划用于测量调度延迟。 */
inline unsigned long long get_ticks(void)
{
// __asm__ ("rdtsc");
return 0ULL;
}
/* 用于支持线程在被取消时的必要清理操作。 */
static void sleepy_wrapper_cleanup(void *voidp)
{
struct thread_worker *worker = voidp;
pthread_mutex_unlock(&worker->lock);
free(worker);
}
/* 这就是线程池内线程的执行函数了。 */
static void* sleepy_wrapper(void *voidp)
{
struct thread_worker *worker = voidp;
while (1) {
pthread_cleanup_push(sleepy_wrapper_cleanup, worker); /* 预设置上一个清理函数,防止线程取消时内存泄漏。 */
pthread_mutex_lock(&worker->lock); /* 空闲线程应该休眠于此,这个mutex在创建thread pool时就锁住了。或者本循环结束时锁住。 */
worker->delay = get_ticks() - worker->delay; /* 暂时无用。 */
if (THWK_F_CLEAN & worker->flags) /* 线程池正在清理本身,所以线程至此就退出了。 */
goto done; /* 你可能觉得这个goto用得有些多余,但如果不这样编译就会提示句法错误,因为pthread_cleanup_{push,pop}是用宏实现的!你可以参考一下它们的实现。 */
worker->flags |= THWK_F_RUNNING; /* 后述。 */
if (worker->arg.action) /* 进行线程实际的工作 */
worker->arg.action(worker->arg.what);
done:
pthread_mutex_unlock(&worker->lock); /* 解锁这个mutex,允许这个thread的下一次使用 */
pthread_cleanup_pop(0);
if (THWK_F_CLEAN & worker->flags) /* 清理线程池 */
break;
pthread_mutex_lock(&worker->lock); /* 先锁住这个锁,以让本循环开头的pthread_mutex_lock()使线程进入休眠。这个调用应该是成功的,否则就会引用deadlock。 */
worker->flags &= ~THWK_F_RUNNING; /* 设计这个标志的意义在于防止有线程激活操作在以上unlock/lock之间发生,如果这样的话,就会引起deadlock,激活操作的实现后述。 */
}
pthread_exit(0);
}
/* 无需废话的函数。 */
pthread_t thread_pool_rawid(struct thread_worker *worker)
{
return worker->id;
}
/* 如果线程被取消了,通知线程池忘记它,目前的实现很简单。*/
void thread_pool_forget(struct thread_pool *pool, struct thread_worker *worker)
{
pool->total--;
}
/* 线程激活操作 */
void thread_pool_activate(struct thread_worker *worker)
{
worker->delay = get_ticks();
while (thread_pool_is_running(worker)) /* 防止出现deadlock */
;
pthread_mutex_unlock(&worker->lock); /* 使sleepy_wrapper()内循环开头部分的lock()操作返回,即线程得以唤醒执行实际的action(what)。 */
}
/* 另一个无须废话的函数 */
int thread_pool_is_running(struct thread_worker *worker)
{
return (worker->flags & THWK_F_RUNNING);
}
/* 从线程池中借出一个线程,其实就是一个从链表头中摘出thread_worker的简单函数 */
int thread_pool_lend(struct thread_pool *pool, void (*action)(void*), void* what, struct thread_worker **worker)
{
if (!action || !pool || !worker)
return -EINVAL;
pthread_mutex_lock(&pool->lock);
*worker = pool->first;
if (worker) {
(*worker)->arg.action = action;
(*worker)->arg.what = what;
pool->first = (*worker)->next;
(*worker)->next = NULL;
pool->current_nr--;
}
pthread_mutex_unlock(&pool->lock);
return 0;
}
/* 向线程池里归还一个thread,头插法插入thread_worker链表。 */
int thread_pool_giveback(struct thread_pool *pool, struct thread_worker *worker)
{
if (!pool || !worker)
return -EINVAL;
while (thread_pool_is_running(worker))
;
pthread_mutex_lock(&pool->lock);
worker->next = pool->first;
pool->first = worker;
worker->arg.action = NULL;
worker->arg.what = NULL;
pool->current_nr++;
pthread_mutex_unlock(&pool->lock);
return 0;
}
/* 虽然有点长,但仍然是无须废话:线程池创建 */
struct thread_pool* thread_pool_create(int nr_to_create, pthread_attr_t *attr)
{
struct thread_pool *pool;
struct thread_worker *worker;
int i, chk;
if (!nr_to_create)
return NULL;
pool = malloc(sizeof(struct thread_pool));
if (!pool)
return NULL;
pool->first = NULL;
pool->total = 0;
pthread_mutex_init(&pool->lock, NULL);
for (i=0; i
if (!worker)
break;
memset(worker, 0, sizeof(struct thread_worker));
pthread_mutex_init(&worker->lock, NULL);
pthread_mutex_lock(&worker->lock);
chk = pthread_create(&worker->id, attr, sleepy_wrapper, (void*)worker);
if (chk) {
pthread_mutex_unlock(&worker->lock);
pthread_mutex_destroy(&worker->lock);
free(worker);
break;
}
worker->next = pool->first;
pool->first = worker;
}
pool->total = i;
pool->current_nr = i;
if (0 == i) {
pthread_mutex_destroy(&pool->lock);
free(pool);
pool = NULL;
}
return pool;
}
/* 清理线程池。 */
int thread_pool_clean(struct thread_pool *pool)
{
struct thread_worker *worker;
pthread_mutex_lock(&pool->lock);
if (pool->total != pool->current_nr) {
pthread_mutex_unlock(&pool->lock);
return -EBUSY;
}
while (NULL != (worker = pool->first)) {
worker->flags = THWK_F_CLEAN; /* this is =, rather than |= ! */
pthread_mutex_unlock(&worker->lock);
pthread_join(worker->id, NULL);
pool->first = worker->next;
pthread_mutex_destroy(&worker->lock);
free(worker);
}
pthread_mutex_unlock(&pool->lock);
pthread_mutex_destroy(&pool->lock);
free(pool);
return 0;
}
(3)main.c:
#include "threadpool.h"
unsigned long long sum(unsigned long long start, unsigned long long end)
{
unsigned long long sum;
sum = 0;
for (; start<=end; ++start)
sum += start;
return sum;
}
struct per_sum {
unsigned long long sum, start, end;
pthread_mutex_t lock;
pthread_cond_t cond;
};
void threaded_sum(void *voidp)
{
struct per_sum *per_sum = voidp;
printf("thread %p start\n", voidp);
if (!per_sum) {
//printf("per_sum == NULL\n");
return;
}
printf("+++++++++++++++start=%lld, end=%lld\n", per_sum->start, per_sum->end);
per_sum->sum = sum(per_sum->start, per_sum->end);
per_sum->start = per_sum->end = 0;
pthread_mutex_lock(&per_sum->lock);
printf("thread %p exit, end=%lld\n", voidp, per_sum->end);
pthread_cond_signal(&per_sum->cond);
pthread_mutex_unlock(&per_sum->lock);
}
int main(void)
{
#define NR_THREADS 2
struct thread_worker* workers[NR_THREADS];
struct per_sum per_sums[NR_THREADS];
struct thread_pool *pool;
int i;
unsigned long long start = 0, end = 0;
unsigned long long result = 0;
unsigned long long delta = 10;
printf("thread_pool is running...\n");
printf("+++++++++++++++init+++++++++++++++\n");
printf("thread_pool create...\n");
pool = thread_pool_create(NR_THREADS, NULL);
if(!pool) exit(-1);
for (i=0; i
printf("failed init mutex\n");
exit(3);
}
if (pthread_cond_init(&per_sums[i].cond, NULL)) {
printf("failed init cond\n");
exit(4);
}
if (thread_pool_lend(pool, threaded_sum, (void*)&per_sums[i], &workers[i])) {
printf("failed to lend thread %d\n", i);
exit(5);
}
}
start = 0;
printf("++++++set value++++result=%lld++++\n", result);
printf("thread_pool activate...\n");
/* activate threads */
for (i=0; i
per_sums[i].end = per_sums[i].start + delta;
start = per_sums[i].end + 1;
thread_pool_activate(workers[i]);
}
printf("thread_pool wait...\n");
for (i=0; i
while (per_sums[i].end != 0)
pthread_cond_wait(&per_sums[i].cond, &per_sums[i].lock);
result += per_sums[i].sum;
pthread_mutex_unlock(&per_sums[i].lock);
}
/* activate threads again */
printf("++++++set value again++++result=%lld++++\n", result);
printf("thread_pool activate again...\n");
for (i=0; i
per_sums[i].end = per_sums[i].start + delta;
start = per_sums[i].end + 1;
thread_pool_activate(workers[i]);
}
end = per_sums[NR_THREADS-1].end;
for (i=0; i
while (per_sums[i].end != 0)
pthread_cond_wait(&per_sums[i].cond, &per_sums[i].lock);
result += per_sums[i].sum;
pthread_mutex_unlock(&per_sums[i].lock);
}
printf("thread_pool giveback...\n");
for (i=0; i
printf("failed to giveback thread %d\n", i);
exit(6);
}
pthread_mutex_destroy(&per_sums[i].lock);
pthread_cond_destroy(&per_sums[i].cond);
}
printf("thread_pool clean...\n");
thread_pool_clean(pool);
printf("*******sum = %lld******\n", result);
return 0;
}
(4)编译运行:
gcc -o main main.c threadpool.c -lpthread
./main
2、简单的线程池代码二
(1)threadpool.c:
#include
#include
#include
#include
#include
#include
typedef struct worker
{
void *(*process) (void *arg);
void *arg;
struct worker *next;
} CThread_worker;
typedef struct
{
pthread_mutex_t queue_lock;
pthread_cond_t queue_ready;
CThread_worker *queue_head;
int shutdown;
pthread_t *threadid;
int max_thread_num;
int cur_queue_size;
} CThread_pool;
int pool_add_worker (void *(*process) (void *arg), void*arg);
void *thread_routine (void *arg);
static CThread_pool *pool = NULL;
void pool_init (int max_thread_num)
{
pool = (CThread_pool *) malloc (sizeof(CThread_pool));
pthread_mutex_init (&(pool->queue_lock), NULL);
pthread_cond_init (&(pool->queue_ready), NULL);
pool->queue_head = NULL;
pool->max_thread_num = max_thread_num;
pool->cur_queue_size = 0;
pool->shutdown = 0;
pool->threadid = (pthread_t *) malloc (max_thread_num * sizeof(pthread_t));
int i = 0;
for (i = 0; i < max_thread_num; i++) {
pthread_create (&(pool->threadid[i]), NULL, thread_routine, NULL);
}
}
int pool_add_worker (void *(*process) (void *arg), void*arg)
{
CThread_worker *newworker = (CThread_worker *) malloc (sizeof(CThread_worker));
newworker->process = process;
newworker->arg = arg;
newworker->next = NULL;
pthread_mutex_lock (&(pool->queue_lock));
CThread_worker *member = pool->queue_head;
if (member != NULL) {
while (member->next != NULL)
member = member->next;
member->next = newworker;
}
else {
pool->queue_head = newworker;
}
assert (pool->queue_head != NULL);
pool->cur_queue_size++;
pthread_mutex_unlock (&(pool->queue_lock));
pthread_cond_signal (&(pool->queue_ready));
return 0;
}
int pool_destroy ()
{
if (pool->shutdown)
return -1;
pool->shutdown = 1;
pthread_cond_broadcast (&(pool->queue_ready));
int i;
for (i = 0; i < pool->max_thread_num; i++)
pthread_join (pool->threadid[i], NULL);
free (pool->threadid);
CThread_worker *head = NULL;
while (pool->queue_head != NULL) {
head = pool->queue_head;
pool->queue_head = pool->queue_head->next;
free (head);
}
pthread_mutex_destroy(&(pool->queue_lock));
pthread_cond_destroy(&(pool->queue_ready));
free (pool);
pool=NULL;
return 0;
}
void *thread_routine (void *arg)
{
printf ("starting thread 0x%x\n", pthread_self ());
while (1) {
pthread_mutex_lock (&(pool->queue_lock));
while (pool->cur_queue_size == 0 && !pool->shutdown) {
printf ("thread 0x%x is waiting\n", pthread_self ());
pthread_cond_wait (&(pool->queue_ready), &(pool->queue_lock));
}
if (pool->shutdown) {
pthread_mutex_unlock (&(pool->queue_lock));
printf ("thread 0x%x will exit\n", pthread_self ());
pthread_exit (NULL);
}
printf ("thread 0x%x is starting to work\n", pthread_self ());
assert (pool->cur_queue_size != 0);
assert (pool->queue_head != NULL);
pool->cur_queue_size--;
CThread_worker *worker = pool->queue_head;
pool->queue_head = worker->next;
pthread_mutex_unlock (&(pool->queue_lock));
(*(worker->process)) (worker->arg);
free (worker);
worker = NULL;
}
pthread_exit (NULL);
}
(2)main.c:
#include
#include
void *myprocess (void *arg)
{
printf ("threadid is 0x%x, working on task %d\n", pthread_self (),*(int *) arg);
sleep (1);
return NULL;
}
int main (int argc, char **argv)
{
pool_init (3);
int *workingnum = (int *) malloc (sizeof (int) * 10);
int i;
for (i = 0; i < 10; i++) {
workingnum[i] = i;
pool_add_worker (myprocess, &workingnum[i]);
}
sleep (5);
pool_destroy ();
free (workingnum);
return 0;
}
(3)编译运行:
gcc -o main main.c threadpool.c -lpthread
./main