在windows下使用请下载pthreads-win32
直接上源码(有比较详细的注释了),
threadpool.h
/*
* Threadpool implementation using pthreads
* Copyright (C) 2011 milkyjing <milkyjing@gmail.com>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* milkyjing
*
*/
#ifndef __THREADPOOL_H__
#define __THREADPOOL_H__
#pragma once
#include <stdlib.h>
#include <memory.h>
#include <semaphore.h>
#include <pthread.h>
#pragma comment(lib, "pthreadVC2.lib")
/* Threadpool status */
#define TP_STAT_INIT 0
#define TP_STAT_RUNNING 1
#define TP_STAT_DOWN 2
typedef void *(*tpw_func)(void *);
typedef void *tpw_args;
/**
* ThreadpoolWork Structure
* a work in the threadpool
*/
typedef struct _thread_pool_work {
tpw_func execution_route; /**< route function of the work */
tpw_args args; /**< parameters of the function */
int args_deepcopy; /**< indicates whether we should free args after we finishing the work or not */
} ThreadpoolWork, *ThreadpoolWorkPtr;
/**
* ThreadpoolWorkQueue Structure
* Work queue that holds works which are being dealt in the thread pool
* queue of circular array implementation
*/
typedef struct _thread_pool_work_queue {
ThreadpoolWorkPtr buf; /**< pointer to the address of the queue (same as the address of the head element) */
int size; /**< size of the maximum works the queue can hold, should be equal to `threads' of the threadpool */
int head_index; /**< head of the queue */
int rear_index; /**< rear of the queue */
} ThreadpoolWorkQueue, *ThreadpoolWorkQueuePtr;
typedef tpw_func tp_thread_init_func; /**< thread entrance function */
/**
* Threadpool Structure
* A threadpool hosts many threads, such threads are also named workers
*/
typedef struct _thread_pool {
int threads; /**< number of threads in the thread pool, i.e. number of workers */
pthread_t *ptids; /**< IDs of threads (workers) in the thread pool */
pthread_mutex_t sync_mutex; /**< mutex for synchronization */
sem_t work_sem; /**< semaphore that indicates works which is under going */
sem_t thread_sem; /**< semaphore that indicates available threads (idle workers) in the thread pool */
tp_thread_init_func thread_entry; /**< entry point for each thread, (the workers share the same door, so there is only one entry point) */
ThreadpoolWorkQueue work_queue; /**< work queue */
int incompleted; /**< incompleted works in the thread pool */
int tp_stat; /**< thread pool status, init, running, down */
int shutdown; /**< shutdown = 1 indicates that the thread pool is about to shutting down */
int completion; /**< set completion = 1 to check if the threadpool has completed all of the tasks assigned to it */
pthread_cond_t completion_cond; /**< condition variable for checking completion of the thread pool */
} Threadpool, *ThreadpoolPtr;
/* Inhibit C++ name-mangling for threadpool functions but not for system calls. */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* Create a new threadpool
* @param ptp pointer to ThreadpoolPtr to hold the return value, can be NULL
* @param size number of threads the threadpool maintains
* @return ThreadpoolPtr
*/
extern ThreadpoolPtr tp_create(ThreadpoolPtr *ptp, int size);
/**
* Initialize the threadpool
* @param tp pointer to the Threadpool which is going to be initialized
* @return int 0 on success
*/
extern int tp_initialize(ThreadpoolPtr tp);
/**
* Assign a work to the threadpool
* @param tp pointer to the Threadpool
* @param pwork pointer to the work that is going to be assigned to the threadpool
* @return int 0 on success
*/
extern int tp_assign_work(ThreadpoolPtr tp, const ThreadpoolWorkPtr pwork);
/**
* Wait for completion of all tasks assigned to the threadpool
* @param tp pointer to the Threadpool
*/
extern void tp_wait(ThreadpoolPtr tp);
/**
* Destroy a threadpool and free the occupied resource
* @param tp pointer to the Threadpool
* @param max_waiting_works indicates the maximum possible works in the waiting queue, i.e., the works that haven't entered the threadpool
* @return int 0 on success
*/
extern int tp_destroy(ThreadpoolPtr tp, int max_waiting_works);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __THREADPOOL_H__ */
threadpool.c
/*
* Copyright (C) 2011 milkyjing <milkyjing@gmail.com>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* milkyjing
*
*/
#include "threadpool.h"
static int _tp_fetch_work(ThreadpoolPtr tp, ThreadpoolWorkPtr pwork) {
sem_wait(&tp->work_sem); /* wait for available work, if no works need to be dealt, it will be suspended */
if (TP_STAT_DOWN == tp->tp_stat) return 0; /* the threadpool is down, no work fetched */
pthread_mutex_lock(&tp->sync_mutex); /* we are going to modify a shared value, so we should lock it */
memcpy(pwork, &tp->work_queue.buf[tp->work_queue.head_index], sizeof(ThreadpoolWork)); /* must be a copy of work in the work_queue, since work_queue may be changed right out of the lock */
tp->work_queue.head_index = (++tp->work_queue.head_index) % tp->work_queue.size; /* remove the fetched work */
pthread_mutex_unlock(&tp->sync_mutex); /* unlock the work_queue */
return 1;
}
/* this function (may have many forks) will be executed right after the tp_initialize function, and stays idle (wait for available work) */
static void *_tp_thread_execution_route(void *args) {
ThreadpoolPtr tp;
ThreadpoolWork work;
tp = (ThreadpoolPtr)args;
while (_tp_fetch_work(tp, &work)) { /* wait until a work comes, if no work comes, suspends */
work.execution_route(work.args); /* do the work */
if (work.args_deepcopy)
free(work.args); /* free the memory occupied by args */
pthread_mutex_lock(&tp->sync_mutex);
if (!tp->shutdown) /* if we are shutting down the threadpool, then we musn't wake up the thread to accept new works */
sem_post(&tp->thread_sem); /* if we are not shutting down the threadpool then, after finishing the work, we must restore the thread (worker) to idle */
tp->incompleted--;
if ((tp->completion || tp->shutdown) && 0 == tp->incompleted)
pthread_cond_signal(&tp->completion_cond);
pthread_mutex_unlock(&tp->sync_mutex);
}
return (void *)0;
}
ThreadpoolPtr tp_create(ThreadpoolPtr *ptp, int size) {
ThreadpoolPtr tp;
tp = (ThreadpoolPtr)malloc(sizeof(Threadpool));
tp->threads = (size < 1) ? 1 : size;
tp->ptids = (pthread_t *)malloc(tp->threads * sizeof(pthread_t));
pthread_mutex_init(&tp->sync_mutex, NULL);
sem_init(&tp->work_sem, 0, 0); /* initially there is no work to be delt */
sem_init(&tp->thread_sem, 0, tp->threads); /* and all workers stay idle */
tp->thread_entry = (tp_thread_init_func)_tp_thread_execution_route; /* the same entrance (start point) for each worker */
tp->work_queue.size = tp->threads;
tp->work_queue.buf = (ThreadpoolWorkPtr)malloc(tp->work_queue.size * sizeof(ThreadpoolWork));
tp->work_queue.head_index = 0;
tp->work_queue.rear_index = 0;
tp->incompleted = 0;
tp->tp_stat = TP_STAT_INIT;
tp->shutdown = 0;
tp->completion = 0;
pthread_cond_init(&tp->completion_cond, NULL);
if (ptp)
*ptp = tp;
return tp;
}
int tp_initialize(ThreadpoolPtr tp) {
int i, j;
if (TP_STAT_INIT != tp->tp_stat) return -1;
for (i = 0; i < tp->threads; i++) {
if (pthread_create(&tp->ptids[i], NULL, tp->thread_entry, tp) != 0) { /* now _tp_thread_execution_route function will have many forks */
tp->tp_stat = TP_STAT_DOWN; /* an error occurs */
for (j = 0; j < i; j++)
sem_post(&tp->work_sem);
return -1;
}
}
tp->tp_stat = TP_STAT_RUNNING;
return 0;
}
int tp_assign_work(ThreadpoolPtr tp, const ThreadpoolWorkPtr pwork) {
if (TP_STAT_RUNNING != tp->tp_stat) return -1;
sem_wait(&tp->thread_sem); /* wait for available thread (worker) of the thread pool */
if (TP_STAT_DOWN == tp->tp_stat) return -1; /* if the thread pool is down before the work being assigned to the thread pool */
pthread_mutex_lock(&tp->sync_mutex);
memcpy(&tp->work_queue.buf[tp->work_queue.rear_index], pwork, sizeof(ThreadpoolWork));
tp->work_queue.rear_index = (++tp->work_queue.rear_index) % tp->work_queue.size;
sem_post(&tp->work_sem); /* inform some thread that a work has come */
tp->incompleted++;
pthread_mutex_unlock(&tp->sync_mutex);
return 0;
}
void tp_wait(ThreadpoolPtr tp) {
tp->completion = 1;
pthread_mutex_lock(&tp->sync_mutex);
if (tp->incompleted > 0) /* if there are still some incompleted works in the thread pool */
pthread_cond_wait(&tp->completion_cond, &tp->sync_mutex);
tp->completion = 0;
pthread_mutex_unlock(&tp->sync_mutex);
}
int tp_destroy(ThreadpoolPtr tp, int max_waiting_works) {
int i;
if (TP_STAT_DOWN == tp->tp_stat) return 0;
else if (TP_STAT_RUNNING == tp->tp_stat) {
tp->shutdown = 1; /* shutting down the threadpool */
pthread_mutex_lock(&tp->sync_mutex);
if (tp->incompleted > 0) /* if there are still some incompleted works in the thread pool */
pthread_cond_wait(&tp->completion_cond, &tp->sync_mutex);
tp->tp_stat = TP_STAT_DOWN; /* only after we finish all the under working tasks can we set the status of the threadpool to DOWN */
for (i = 0; i < max_waiting_works; i++)
sem_post(&tp->thread_sem); /* cancel the waiting works (note that now shutdown == true) */
for (i = 0; i < tp->threads; i++)
sem_post(&tp->work_sem); /* wake up the idle threads */
for (i = 0; i < tp->threads; i++)
pthread_join(tp->ptids[i], NULL); /* wait for completion of the works in the thread pool */
pthread_mutex_unlock(&tp->sync_mutex);
}
pthread_mutex_destroy(&tp->sync_mutex);
sem_destroy(&tp->work_sem);
sem_destroy(&tp->thread_sem);
pthread_cond_destroy(&tp->completion_cond);
free(tp->ptids);
free(tp->work_queue.buf);
free(tp);
return 0;
}
test_threadpool.c
/*
* Copyright (C) 2011 milkyjing <milkyjing@gmail.com>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* milkyjing
*
*/
#include "threadpool.h"
#include <stdio.h>
#include <Windows.h>
void *pp(void *args) {
int i;
const char *str = (const char *)args;
for (i = 0; i < 10; i++) {
printf("%05i %s\n", i, str);
Sleep(1000);
}
return (void *)0;
}
void *aw(void *args) {
ThreadpoolPtr tp;
ThreadpoolWorkPtr pwork;
tp = (ThreadpoolPtr)((unsigned long *)args)[0];
pwork = (ThreadpoolWorkPtr)((unsigned long *)args)[1];
tp_assign_work(tp, pwork);
return (void *)0;
}
int main() {
pthread_t tid;
char sz1[1024];
char sz2[1024];
unsigned long aa[2];
ThreadpoolPtr tp;
ThreadpoolWork work;
tp = tp_create(NULL, 3);
if (tp_initialize(tp) != 0) {
tp_destroy(tp, 0);
return 0;
}
work.execution_route = (tpw_func)pp;
memcpy(sz1, "Nice to meet you!", 500);
work.args = (tpw_args)sz1;
tp_assign_work(tp, &work);
memcpy(sz2, "Nice to meet you, too!", 500);
work.args = (tpw_args)sz2;
tp_assign_work(tp, &work);
tp_assign_work(tp, &work);
aa[0] = (unsigned long)tp;
aa[1] = (unsigned long)&work;
pthread_create(&tid, NULL, aw, (void *)aa);
//tp_assign_work(tp, &work);
printf("Destroy: %i\n", tp_destroy(tp, 1));
return 0;
}