├── Makefile ├── async_cond_queue.c ├── async_eventfd_queue.c ├── async_queue_interner.h ├── cond_mutex.png ├── fuck.png ├── lijiao.jpg ├── multiple.png ├── queue.c ├── queue.h ├── test_performance.c ├── thread_pool.c └── thread_pool.h /Makefile: -------------------------------------------------------------------------------- 1 | SOURCES=$(wildcard *.c) 2 | OBJECTS=$(patsubst %.c,%.o,$(SOURCES)) 3 | CC=gcc 4 | PROGRAM=threadpool_test 5 | CFLAGS=-lpthread 6 | 7 | $(PROGRAM) : $(OBJECTS) 8 | $(CC) -o $(PROGRAM) $(OBJECTS) $(CFLAGS) 9 | $(OBJECTS):$(SOURCES) 10 | 11 | .PHONY : clean 12 | clean : 13 | rm -rf $(OBJECTS) $(USE_EVENT_PROGRAM) $(USE_COND_PROGRAM) 14 | -------------------------------------------------------------------------------- /async_cond_queue.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhiyong0804/f-threadpool/91aba6374b97c25b00e69978ad4a4904cc88bfa3/async_cond_queue.c -------------------------------------------------------------------------------- /async_eventfd_queue.c: -------------------------------------------------------------------------------- 1 | 2 | #include "queue.h" 3 | //#include "async_queue_interner.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "systime.h" 13 | #include "queue.h" 14 | #include "async_queue_interner.h" 15 | 16 | 17 | 18 | #define MAX_EVENTS 1024 19 | 20 | 21 | static async_queue_t* async_eventfd_queue_create(int size); 22 | static BOOL async_eventfd_queue_push_tail(async_queue_t* q, task_t* data); 23 | static task_t* async_eventfd_queue_pop_head(async_queue_t* q, int timeout); 24 | static void async_eventfd_queue_free(async_queue_t* q); 25 | static BOOL async_eventfd_queue_empty(async_queue_t* q); 26 | static BOOL async_eventfd_queue_destory(async_queue_t* q); 27 | 28 | 29 | const async_queue_op_t async_eventfd_op = 30 | { 31 | "eventfd", 32 | async_eventfd_queue_create, 33 | async_eventfd_queue_push_tail, 34 | async_eventfd_queue_pop_head, 35 | async_eventfd_queue_free, 36 | async_eventfd_queue_empty, 37 | async_eventfd_queue_destory 38 | }; 39 | 40 | static time_t start_stm = 0; 41 | 42 | async_queue_t *async_eventfd_queue_create(int size) 43 | { 44 | async_queue_t* q = (async_queue_t*)malloc(sizeof (async_queue_t)); 45 | 46 | q->queue = queue_create(size); 47 | q->epollfd = epoll_create1(0); 48 | q->tasked = 0; 49 | if (q->epollfd == -1) 50 | { 51 | return NULL; 52 | } 53 | 54 | start_stm = get_current_timestamp(); 55 | 56 | return q; 57 | } 58 | 59 | BOOL async_eventfd_queue_push_tail(async_queue_t* q, task_t *task) 60 | { 61 | unsigned long long i = 0xffffffff; 62 | if (!queue_is_full(q->queue)) 63 | { 64 | queue_push_tail(q->queue, task); 65 | 66 | struct epoll_event ev; 67 | int efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); 68 | if (efd == -1) printf("eventfd create: %s", strerror(errno)); 69 | ev.events = EPOLLIN ;// | EPOLLLT; 70 | ev.data.fd = efd; 71 | if (epoll_ctl(q->epollfd, EPOLL_CTL_ADD, efd, &ev) == -1) 72 | { 73 | return NULL; 74 | } 75 | 76 | write(efd, &i, sizeof (i)); 77 | 78 | return TRUE; 79 | } 80 | 81 | return FALSE; 82 | } 83 | 84 | task_t* async_eventfd_queue_pop_head(async_queue_t* q, int timeout) 85 | { 86 | unsigned long long i = 0; 87 | struct epoll_event events[MAX_EVENTS]; 88 | int nfds = epoll_wait(q->epollfd, events, MAX_EVENTS, -1); 89 | if (nfds == -1) 90 | { 91 | return NULL; 92 | } 93 | else 94 | { 95 | read(events[0].data.fd, &i, sizeof (i)); 96 | close(events[0].data.fd); // NOTE: need to close here 97 | task_t* task = queue_pop_head(q->queue); 98 | 99 | if (task) 100 | { 101 | q->tasked ++; 102 | static long long precision = 10; 103 | if ((q->tasked % precision ) == 0) 104 | { 105 | time_t current_stm = get_current_timestamp(); 106 | printf("%d tasks cost : %d\n", precision, current_stm - start_stm); 107 | precision *= 10; 108 | } 109 | } 110 | return task; 111 | } 112 | 113 | return NULL; 114 | } 115 | 116 | void async_eventfd_queue_free(async_queue_t *q) 117 | { 118 | queue_free(q->queue); 119 | close(q->efd); 120 | free(q); 121 | } 122 | 123 | BOOL async_eventfd_queue_empty(async_queue_t* q) 124 | { 125 | return queue_is_empty(q->queue); 126 | } 127 | 128 | BOOL async_eventfd_queue_destory(async_queue_t* q) 129 | { 130 | return TRUE; 131 | } 132 | 133 | -------------------------------------------------------------------------------- /async_queue_interner.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhiyong0804/f-threadpool/91aba6374b97c25b00e69978ad4a4904cc88bfa3/async_queue_interner.h -------------------------------------------------------------------------------- /cond_mutex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhiyong0804/f-threadpool/91aba6374b97c25b00e69978ad4a4904cc88bfa3/cond_mutex.png -------------------------------------------------------------------------------- /fuck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhiyong0804/f-threadpool/91aba6374b97c25b00e69978ad4a4904cc88bfa3/fuck.png -------------------------------------------------------------------------------- /lijiao.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhiyong0804/f-threadpool/91aba6374b97c25b00e69978ad4a4904cc88bfa3/lijiao.jpg -------------------------------------------------------------------------------- /multiple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhiyong0804/f-threadpool/91aba6374b97c25b00e69978ad4a4904cc88bfa3/multiple.png -------------------------------------------------------------------------------- /queue.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "queue.h" 4 | 5 | #define DEFAULT_QUEUE_SIZE 65535 6 | 7 | 8 | queue_t *queue_create(int size) 9 | { 10 | queue_t *q = (queue_t*)malloc(sizeof (queue_t)); 11 | if (q != NULL) 12 | { 13 | if (size > 0) 14 | { 15 | q->tasks = (task_t*)malloc(size * sizeof(task_t)); 16 | q->capcity = size; 17 | } 18 | else 19 | { 20 | int default_size = DEFAULT_QUEUE_SIZE; 21 | q->tasks =(task_t*)malloc(default_size * sizeof(task_t)); 22 | q->capcity = default_size; 23 | } 24 | 25 | if (q->tasks == NULL) 26 | { 27 | free(q); 28 | return NULL; 29 | } 30 | 31 | q->head = q->tail = q->size = 0; 32 | } 33 | 34 | return q; 35 | } 36 | 37 | BOOL queue_is_full(queue_t *q) 38 | { 39 | return q->size == q->capcity; 40 | } 41 | 42 | BOOL queue_is_empty(queue_t *q) 43 | { 44 | return q->size == 0; 45 | } 46 | 47 | BOOL queue_push_tail(queue_t* q, task_t* t) 48 | { 49 | if (!queue_is_full(q)) 50 | { 51 | q->tasks[q->tail].run = t->run; 52 | q->tasks[q->tail].argv = t->argv; 53 | q->tail = (q->tail + 1) % q->capcity; 54 | q->size++; 55 | return TRUE; 56 | } 57 | 58 | return FALSE; 59 | } 60 | 61 | task_t* queue_pop_head(queue_t* q) 62 | { 63 | task_t* t= NULL; 64 | if (!queue_is_empty(q)) 65 | { 66 | t = &q->tasks[(q->head)]; 67 | q->head = (q->head + 1) % q->capcity; 68 | q->size--; 69 | } 70 | return t; 71 | } 72 | 73 | void queue_free(queue_t* q) 74 | { 75 | free(q->tasks); 76 | free(q); 77 | } 78 | 79 | -------------------------------------------------------------------------------- /queue.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _THREAD_POOL_QUEUE_H_ 3 | #define _THREAD_POOL_QUEUE_H_ 4 | 5 | #include "glo_def.h" 6 | 7 | /** 8 | * @struct task_t 9 | * @brief the work struct 10 | * 11 | * @var run Pointer to the function that will perform the task. 12 | * @var argv Argument to be passed to the run function. 13 | */ 14 | typedef struct queue_task 15 | { 16 | void* (*run)(void *); 17 | void* argv; 18 | }task_t; 19 | 20 | typedef struct queue 21 | { 22 | int head; 23 | int tail; 24 | int size; 25 | int capcity; 26 | task_t* tasks; 27 | } queue_t; 28 | 29 | // API 30 | 31 | /** 32 | * @function threadpool_create 33 | * @brief create queue 34 | * @param size the size of queue 35 | * @return created queue 36 | */ 37 | queue_t *queue_create(int size); 38 | 39 | BOOL queue_is_full(queue_t* q); 40 | 41 | BOOL queue_is_empty(queue_t* q); 42 | 43 | BOOL queue_push_tail(queue_t* q, task_t* data); 44 | 45 | task_t* queue_pop_head(queue_t* q); 46 | 47 | void queue_free(queue_t* q); 48 | 49 | 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /test_performance.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "systime.h" 6 | 7 | #include "thread_pool.h" 8 | 9 | static void* write_file(void* argv) 10 | { 11 | char path[128] = {0}; 12 | int file_index = *((int*)argv); 13 | 14 | //please check there is test directory under your current directory 15 | snprintf(path, 128, "test/%d.txt", file_index); 16 | 17 | FILE* file_ptr = fopen(path, "w+"); 18 | if (file_ptr != NULL) 19 | { 20 | char *str="hello,I am a test program!\r\n"; 21 | for(int i = 0; i < 100; i++) 22 | { 23 | fwrite(str, sizeof(char), strlen(str), file_ptr); 24 | } 25 | 26 | fclose(file_ptr); 27 | } 28 | else 29 | { 30 | printf("write file failed.\n"); 31 | } 32 | 33 | return NULL; 34 | } 35 | 36 | int main(int argc, char** argv) 37 | { 38 | if (argc != 2) 39 | { 40 | printf("usage: %s count.\n", argv[0]); 41 | return -1; 42 | } 43 | 44 | int task_count = atoi(argv[1]); 45 | 46 | time_t start = get_current_timestamp(); 47 | 48 | threadpool_t* pool = threadpool_create(8, 65535, 0); 49 | 50 | int* file_index = (int*)malloc(sizeof(int) * task_count); 51 | for (int i = 0; i < task_count; i ++) 52 | { 53 | file_index[i] = i; 54 | } 55 | 56 | for (int i = 0 ; i < task_count; i ++) 57 | { 58 | if(0 != threadpool_add(pool, write_file, (void*)&file_index[i])) 59 | { 60 | printf("add task to thread pool failed.\n"); 61 | } 62 | } 63 | 64 | #if 1 65 | if (0 != threadpool_destroy(pool, graceful_shutdown)) 66 | { 67 | printf("destroy thread pool failed.\n"); 68 | } 69 | #endif 70 | 71 | time_t end = get_current_timestamp(); 72 | printf("cost: %d\n", end - start); 73 | 74 | for(;;); 75 | 76 | return 0; 77 | } 78 | 79 | 80 | -------------------------------------------------------------------------------- /thread_pool.c: -------------------------------------------------------------------------------- 1 | 2 | #include "thread_pool.h" 3 | #include "async_queue_interner.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | extern const async_queue_op_t async_eventfd_op; 10 | extern const async_queue_op_t async_cond_op; 11 | 12 | static const async_queue_op_t* async_queue_ops[] = 13 | { 14 | &async_cond_op, 15 | &async_eventfd_op 16 | }; 17 | 18 | static threadpool_type_t threadpool_current = threadpool_cond_type; 19 | 20 | /** 21 | * @function void *threadpool_thread(void *threadpool) 22 | * @brief the worker thread 23 | * @param threadpool the pool which own the thread 24 | */ 25 | static void* threadpool_thread(void *threadpool); 26 | 27 | int threadpool_free(threadpool_t *pool); 28 | 29 | 30 | BOOL threadpool_config(threadpool_type_t type) 31 | { 32 | threadpool_current = type; 33 | return TRUE; 34 | } 35 | 36 | threadpool_t* threadpool_create(int thread_count, int queue_size, int flags) 37 | { 38 | threadpool_t* pool; 39 | 40 | if ((thread_count <=0) || (thread_count > MAX_THREAD_POOL_SIZE) || (queue_size <=0) || (queue_size > MAX_QUEUE_SIZE)) 41 | { 42 | printf("please check your input parameters.\n"); 43 | return NULL; 44 | } 45 | 46 | if ((pool = (threadpool_t*)malloc(sizeof(threadpool_t))) == NULL) 47 | { 48 | printf("malloc threadpool memory failed.\n"); 49 | return NULL; 50 | } 51 | 52 | pool->qsize = queue_size; 53 | pool->tsize = thread_count; 54 | pool->qop = async_queue_ops[threadpool_current]; 55 | pool->threads = (pthread_t*)malloc(sizeof(pthread_t) * thread_count); 56 | pool->queue = (pool->qop->create)(queue_size); 57 | pool->shutdown = 0; 58 | 59 | // create worker thread 60 | for(int i = 0; i < pool->tsize; i ++) 61 | { 62 | if(pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void*)pool) != 0) 63 | { 64 | printf("create thread failed.\n"); 65 | threadpool_destroy(pool, 0); 66 | return NULL; 67 | } 68 | } 69 | 70 | pool->started = TRUE; 71 | 72 | return pool; 73 | } 74 | 75 | int threadpool_add(threadpool_t *pool, void* (*routine)(void *), void *arg) 76 | { 77 | int err = 0; 78 | 79 | if(pool == NULL || routine == NULL) 80 | { 81 | return threadpool_invalid; 82 | } 83 | 84 | do { 85 | /* Are we shutting down ? */ 86 | if(pool->shutdown) 87 | { 88 | err = threadpool_shutdown; 89 | break; 90 | } 91 | 92 | task_t task; 93 | task.run = routine; 94 | task.argv = arg; 95 | 96 | if (!pool->qop->push(pool->queue, &task)) 97 | { 98 | printf("push task to queue failed.\n"); 99 | } 100 | 101 | } while(0); 102 | 103 | return err; 104 | } 105 | 106 | int threadpool_destroy(threadpool_t *pool, int flags) 107 | { 108 | int i, err = 0; 109 | 110 | if(pool == NULL) 111 | { 112 | return threadpool_invalid; 113 | } 114 | 115 | do { 116 | /* Already shutting down */ 117 | if(pool->shutdown) 118 | { 119 | err = threadpool_shutdown; 120 | break; 121 | } 122 | 123 | pool->shutdown = (flags & threadpool_graceful) ? 124 | graceful_shutdown : immediate_shutdown; 125 | 126 | /* Destory all worker threads */ 127 | pool->qop->destory(pool->queue); 128 | 129 | /* Join all worker thread */ 130 | for(i = 0; i < pool->tsize; i++) 131 | { 132 | if(pthread_join(pool->threads[i], NULL) != 0) 133 | { 134 | err = threadpool_thread_failure; 135 | } 136 | } 137 | } while(0); 138 | 139 | /* Only if everything went well do we deallocate the pool */ 140 | if(!err) 141 | { 142 | threadpool_free(pool); 143 | } 144 | 145 | return err; 146 | } 147 | 148 | int threadpool_free(threadpool_t *pool) 149 | { 150 | if(pool == NULL || pool->started > 0) 151 | { 152 | return -1; 153 | } 154 | 155 | if(pool->threads) 156 | { 157 | free(pool->threads); 158 | pool->qop->free(pool->queue); 159 | } 160 | free(pool); 161 | 162 | return 0; 163 | } 164 | 165 | static void *threadpool_thread(void *threadpool) 166 | { 167 | threadpool_t *pool = (threadpool_t *)threadpool; 168 | 169 | for(;;) 170 | { 171 | task_t* task = pool->qop->pop(pool->queue, 50); 172 | if (task != NULL) 173 | { 174 | task->run(task->argv); 175 | } 176 | 177 | if(((pool->shutdown == immediate_shutdown) || (pool->shutdown == graceful_shutdown) ) 178 | && (pool->qop->empty(pool->queue))) 179 | { 180 | //printf("--- thread %d is exit.\n", pthread_self()); 181 | break; 182 | } 183 | } 184 | return(NULL); 185 | } 186 | 187 | 188 | 189 | 190 | -------------------------------------------------------------------------------- /thread_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef _THREAD_POOL_H_ 2 | #define _THREAD_POOL_H_ 3 | 4 | #include 5 | #include "async_queue_interner.h" 6 | #include "glo_def.h" 7 | 8 | /** 9 | * @file threadpool.h 10 | * @brief Threadpool Header File 11 | */ 12 | 13 | /** 14 | * Increase this constants at your own risk 15 | * Large values might slow down your system 16 | */ 17 | #define MAX_THREAD_POOL_SIZE 128 18 | #define MAX_QUEUE_SIZE 65536 19 | 20 | 21 | //typedef struct threadpool_t threadpool_t; 22 | 23 | /** 24 | * @struct threadpool 25 | * @brief The threadpool struct 26 | * 27 | * @var threads Array containing worker threads ID. 28 | * @var tsize Number of threads 29 | * @var queue Array containing the task queue. 30 | * @var qsize Size of the task queue. 31 | * @var shutdown Flag indicating if the pool is shutting down 32 | * @var started Number of started threads 33 | */ 34 | typedef struct threadpool { 35 | pthread_t* threads; 36 | const async_queue_op_t* qop; 37 | async_queue_t* queue; 38 | int qsize; 39 | int tsize; 40 | int shutdown; 41 | int started; 42 | }threadpool_t; 43 | 44 | typedef enum 45 | { 46 | threadpool_cond_type = 0, 47 | threadpool_eventfd_type = 1 48 | }threadpool_type_t; 49 | 50 | typedef enum 51 | { 52 | threadpool_invalid = -1, 53 | threadpool_lock_failure = -2, 54 | threadpool_queue_full = -3, 55 | threadpool_shutdown = -4, 56 | threadpool_thread_failure = -5, 57 | threadpool_memory_error = -6 58 | } threadpool_error_t; 59 | 60 | typedef enum 61 | { 62 | immediate_shutdown = 1, 63 | graceful_shutdown = 2 64 | } threadpool_shutdown_t; 65 | 66 | typedef enum 67 | { 68 | threadpool_graceful = 1 69 | } threadpool_destroy_flags_t; 70 | 71 | #ifdef __cplusplus 72 | extern "C" { 73 | #endif 74 | 75 | BOOL threadpool_config(threadpool_type_t type); 76 | 77 | /** 78 | * @function threadpool_create 79 | * @brief Creates a threadpool_t object. 80 | * @param thread_count Number of worker threads. 81 | * @param queue_size Size of the queue. 82 | * @param flags Unused parameter. 83 | * @return a newly created thread pool or NULL 84 | */ 85 | threadpool_t* threadpool_create(int thread_count, int queue_size, int flags); 86 | 87 | /** 88 | * @function threadpool_add 89 | * @brief add a new task in the queue of a thread pool 90 | * @param pool Thread pool to which add the task. 91 | * @param function Pointer to the function that will perform the task. 92 | * @param argument Argument to be passed to the function. 93 | * @param flags Unused parameter. 94 | * @return 0 if all goes well, negative values in case of error (@see 95 | * threadpool_error_t for codes). 96 | */ 97 | int threadpool_add(threadpool_t *pool, void* (*routine)(void *), void *arg); 98 | 99 | /** 100 | * @function threadpool_destroy 101 | * @brief Stops and destroys a thread pool. 102 | * @param pool Thread pool to destroy. 103 | * @param flags Flags for shutdown 104 | * 105 | * Known values for flags are 0 (default) and threadpool_graceful in 106 | * which case the thread pool doesn't accept any new tasks but 107 | * processes all pending tasks before shutdown. 108 | */ 109 | int threadpool_destroy(threadpool_t *pool, int flags); 110 | 111 | #ifdef __cplusplus 112 | } 113 | #endif 114 | 115 | 116 | #endif 117 | 118 | --------------------------------------------------------------------------------