├── README.md ├── thread_pool_active.c └── thread_pool_simple.c /README.md: -------------------------------------------------------------------------------- 1 | # threadpool 2 | 3 | thread_pool_simple.c : 简单的线程池 4 | gcc -o thread_pool_simple thread_pool_simple.c -lpthread 5 | 6 | thread_pool_active.c : 复杂的线程池,可以直接复用的。 7 | gcc -o thread_pool_active thread_pool_active.c -lpthread 8 | -------------------------------------------------------------------------------- /thread_pool_active.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Author: WangBoJing 4 | * email: 1989wangbojing@gmail.com 5 | * github: https://github.com/wangbojing 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | 21 | typedef void (*JOB_CALLBACK)(void *); 22 | 23 | typedef struct NJOB { 24 | struct NJOB *next; 25 | JOB_CALLBACK func; 26 | void *arg; 27 | } nJob; 28 | 29 | typedef struct NWORKER { 30 | struct NWORKER *active_next; 31 | pthread_t active_tid; 32 | } nWorker; 33 | 34 | typedef struct NTHREADPOOL { 35 | struct NTHREADPOOL *forw; 36 | struct NTHREADPOOL *back; 37 | 38 | pthread_mutex_t mtx; 39 | 40 | pthread_cond_t busycv; 41 | pthread_cond_t workcv; 42 | pthread_cond_t waitcv; 43 | 44 | nWorker *active; 45 | nJob *head; 46 | nJob *tail; 47 | 48 | pthread_attr_t attr; 49 | 50 | int flags; 51 | unsigned int linger; 52 | 53 | int minimum; 54 | int maximum; 55 | int nthreads; 56 | int idle; 57 | 58 | } nThreadPool; 59 | 60 | static void* ntyWorkerThread(void *arg); 61 | 62 | #define NTY_POOL_WAIT 0x01 63 | #define NTY_POOL_DESTROY 0x02 64 | 65 | 66 | static pthread_mutex_t nty_pool_lock = PTHREAD_MUTEX_INITIALIZER; 67 | static sigset_t fillset; 68 | nThreadPool *thread_pool = NULL; 69 | 70 | 71 | 72 | static int ntyWorkerCreate(nThreadPool *pool) { 73 | 74 | sigset_t oset; 75 | pthread_t thread_id; 76 | 77 | pthread_sigmask(SIG_SETMASK, &fillset, &oset); 78 | int error = pthread_create(&thread_id, &pool->attr, ntyWorkerThread, pool); 79 | pthread_sigmask(SIG_SETMASK, &oset, NULL); 80 | 81 | return error; 82 | } 83 | 84 | static void ntyWorkerCleanup(nThreadPool * pool) { 85 | 86 | --pool->nthreads; 87 | 88 | if (pool->flags & NTY_POOL_DESTROY) { 89 | if (pool->nthreads == 0) { 90 | pthread_cond_broadcast(&pool->busycv); 91 | } 92 | } else if (pool->head != NULL && pool->nthreads < pool->maximum && ntyWorkerCreate(pool) == 0) { 93 | pool->nthreads ++; 94 | } 95 | pthread_mutex_unlock(&pool->mtx); 96 | 97 | } 98 | 99 | 100 | static void ntyNotifyWaiters(nThreadPool *pool) { 101 | 102 | if (pool->head == NULL && pool->active == NULL) { 103 | pool->flags &= ~NTY_POOL_WAIT; 104 | pthread_cond_broadcast(&pool->waitcv); 105 | } 106 | } 107 | 108 | 109 | static void ntyJobCleanup(nThreadPool *pool) { 110 | 111 | pthread_t tid = pthread_self(); 112 | nWorker *activep; 113 | nWorker **activepp; 114 | 115 | pthread_mutex_lock(&pool->mtx); 116 | 117 | for (activepp = &pool->active;(activep = *activepp) != NULL;activepp = &activep->active_next) { 118 | *activepp = activep->active_next; 119 | break; 120 | } 121 | 122 | if (pool->flags & NTY_POOL_WAIT) ntyNotifyWaiters(pool); 123 | 124 | } 125 | 126 | static void* ntyWorkerThread(void *arg) { 127 | 128 | nThreadPool *pool = (nThreadPool*)arg; 129 | nWorker active; 130 | 131 | int timeout; 132 | struct timespec ts; 133 | JOB_CALLBACK func; 134 | 135 | pthread_mutex_lock(&pool->mtx); 136 | pthread_cleanup_push(ntyWorkerCleanup, pool); 137 | 138 | active.active_tid = pthread_self(); 139 | 140 | while (1) { 141 | 142 | pthread_sigmask(SIG_SETMASK, &fillset, NULL); 143 | pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); 144 | pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 145 | 146 | timeout = 0; 147 | pool->idle ++; 148 | 149 | if (pool->flags & NTY_POOL_WAIT) { 150 | ntyNotifyWaiters(pool); 151 | } 152 | 153 | while (pool->head == NULL && !(pool->flags & NTY_POOL_DESTROY)) { 154 | 155 | if (pool->nthreads <= pool->minimum) { 156 | 157 | pthread_cond_wait(&pool->workcv, &pool->mtx); 158 | 159 | } else { 160 | 161 | clock_gettime(CLOCK_REALTIME, &ts); 162 | ts.tv_sec += pool->linger; 163 | 164 | if (pool->linger == 0 || pthread_cond_timedwait(&pool->workcv, &pool->mtx, &ts) == ETIMEDOUT) { 165 | timeout = 1; 166 | break; 167 | } 168 | } 169 | } 170 | 171 | pool->idle --; 172 | if (pool->flags & NTY_POOL_DESTROY) break; 173 | 174 | nJob *job = pool->head; 175 | if (job != NULL) { 176 | timeout = 0; 177 | func = job->func; 178 | 179 | void *job_arg = job->arg; 180 | pool->head = job->next; 181 | 182 | if (job == pool->tail) { 183 | pool->tail = NULL; 184 | } 185 | active.active_next = pool->active; 186 | pool->active = &active; 187 | 188 | pthread_mutex_unlock(&pool->mtx); 189 | 190 | pthread_cleanup_push(ntyJobCleanup, pool); 191 | 192 | free(job); 193 | func(job_arg); 194 | 195 | pthread_cleanup_pop(1); 196 | } 197 | 198 | if (timeout && (pool->nthreads > pool->minimum)) { 199 | break; 200 | } 201 | 202 | } 203 | 204 | pthread_cleanup_pop(1); 205 | 206 | return NULL; 207 | 208 | } 209 | 210 | 211 | static void ntyCloneAttributes(pthread_attr_t *new_attr, pthread_attr_t *old_attr) { 212 | 213 | struct sched_param param; 214 | void *addr; 215 | size_t size; 216 | int value; 217 | 218 | pthread_attr_init(new_attr); 219 | 220 | if (old_attr != NULL) { 221 | pthread_attr_getstack(old_attr, &addr, &size); 222 | pthread_attr_setstack(new_attr, NULL, size); 223 | 224 | pthread_attr_getscope(old_attr, &value); 225 | pthread_attr_setscope(new_attr, value); 226 | 227 | pthread_attr_getinheritsched(old_attr, &value); 228 | pthread_attr_setinheritsched(new_attr, value); 229 | 230 | pthread_attr_getschedpolicy(old_attr, &value); 231 | pthread_attr_setschedpolicy(new_attr, value); 232 | 233 | pthread_attr_getschedparam(old_attr, ¶m); 234 | pthread_attr_setschedparam(new_attr, ¶m); 235 | 236 | pthread_attr_getguardsize(old_attr, &size); 237 | pthread_attr_setguardsize(new_attr, size); 238 | } 239 | 240 | pthread_attr_setdetachstate(new_attr, PTHREAD_CREATE_DETACHED); 241 | 242 | } 243 | 244 | 245 | 246 | nThreadPool *ntyThreadPoolCreate(int min_threads, int max_threads, int linger, pthread_attr_t *attr) { 247 | 248 | sigfillset(&fillset); 249 | if (min_threads > max_threads || max_threads < 1) { 250 | errno = EINVAL; 251 | return NULL; 252 | } 253 | 254 | nThreadPool *pool = (nThreadPool*)malloc(sizeof(nThreadPool)); 255 | if (pool == NULL) { 256 | errno = ENOMEM; 257 | return NULL; 258 | } 259 | 260 | pthread_mutex_init(&pool->mtx, NULL); 261 | 262 | pthread_cond_init(&pool->busycv, NULL); 263 | pthread_cond_init(&pool->workcv, NULL); 264 | pthread_cond_init(&pool->waitcv, NULL); 265 | 266 | pool->active = NULL; 267 | pool->head = NULL; 268 | pool->tail = NULL; 269 | pool->flags = 0; 270 | pool->linger = linger; 271 | pool->minimum = min_threads; 272 | pool->maximum = max_threads; 273 | pool->nthreads = 0; 274 | pool->idle = 0; 275 | 276 | ntyCloneAttributes(&pool->attr, attr); 277 | 278 | pthread_mutex_lock(&nty_pool_lock); 279 | 280 | if (thread_pool == NULL) { 281 | 282 | pool->forw = pool; 283 | pool->back = pool; 284 | 285 | thread_pool = pool; 286 | 287 | } else { 288 | 289 | thread_pool->back->forw = pool; 290 | pool->forw = thread_pool; 291 | pool->back = thread_pool->back; 292 | thread_pool->back = pool; 293 | 294 | } 295 | 296 | pthread_mutex_unlock(&nty_pool_lock); 297 | 298 | return pool; 299 | 300 | } 301 | 302 | 303 | int ntyThreadPoolQueue(nThreadPool *pool, JOB_CALLBACK func, void *arg) { 304 | 305 | 306 | nJob *job = (nJob*)malloc(sizeof(nJob)); 307 | if (job == NULL) { 308 | errno = ENOMEM; 309 | return -1; 310 | } 311 | 312 | job->next = NULL; 313 | job->func = func; 314 | job->arg = arg; 315 | 316 | pthread_mutex_lock(&pool->mtx); 317 | 318 | if (pool->head == NULL) { 319 | pool->head = job; 320 | } else { 321 | pool->tail->next = job; 322 | } 323 | pool->tail = job; 324 | 325 | if (pool->idle > 0) { 326 | pthread_cond_signal(&pool->workcv); 327 | } else if (pool->nthreads < pool->maximum && ntyWorkerCreate(pool) == 0) { 328 | pool->nthreads ++; 329 | } 330 | 331 | pthread_mutex_unlock(&pool->mtx); 332 | 333 | return 0; 334 | 335 | } 336 | 337 | 338 | void nThreadPoolWait(nThreadPool *pool) { 339 | 340 | pthread_mutex_lock(&pool->mtx); 341 | 342 | pthread_cleanup_push(pthread_mutex_unlock, &pool->mtx); 343 | 344 | while (pool->head != NULL || pool->active != NULL) { 345 | pool->flags |= NTY_POOL_WAIT; 346 | pthread_cond_wait(&pool->waitcv, &pool->mtx); 347 | } 348 | 349 | pthread_cleanup_pop(1); 350 | } 351 | 352 | 353 | void nThreadPoolDestroy(nThreadPool *pool) { 354 | 355 | nWorker *activep; 356 | nJob *job; 357 | 358 | pthread_mutex_lock(&pool->mtx); 359 | pthread_cleanup_push(pthread_mutex_unlock, &pool->mtx); 360 | 361 | pool->flags |= NTY_POOL_DESTROY; 362 | pthread_cond_broadcast(&pool->workcv); 363 | 364 | for (activep = pool->active;activep != NULL;activep = activep->active_next) { 365 | pthread_cancel(activep->active_tid); 366 | } 367 | 368 | while (pool->nthreads != 0) { 369 | pthread_cond_wait(&pool->busycv, &pool->mtx); 370 | } 371 | 372 | pthread_cleanup_pop(1); 373 | 374 | pthread_mutex_lock(&nty_pool_lock); 375 | 376 | if (thread_pool == pool) { 377 | thread_pool = pool->forw; 378 | } 379 | 380 | if (thread_pool == pool) { 381 | thread_pool = NULL; 382 | } else { 383 | pool->back->forw = pool->forw; 384 | pool->forw->back = pool->back; 385 | } 386 | 387 | pthread_mutex_unlock(&nty_pool_lock); 388 | 389 | for (job = pool->head;job != NULL;job = pool->head) { 390 | pool->head = job->next; 391 | free(job); 392 | } 393 | 394 | pthread_attr_destroy(&pool->attr); 395 | free(pool); 396 | } 397 | 398 | 399 | /********************************* debug thread pool *********************************/ 400 | 401 | 402 | 403 | void king_counter(void *arg) { 404 | 405 | int index = *(int*)arg; 406 | 407 | printf("index : %d, selfid : %lu\n", index, pthread_self()); 408 | 409 | free(arg); 410 | usleep(1); 411 | } 412 | 413 | 414 | #define KING_COUNTER_SIZE 1000 415 | 416 | int main(int argc, char *argv[]) { 417 | 418 | nThreadPool *pool = ntyThreadPoolCreate(10, 20, 15, NULL); 419 | 420 | int i = 0; 421 | for (i = 0;i < KING_COUNTER_SIZE;i ++) { 422 | int *index = (int*)malloc(sizeof(int)); 423 | 424 | memset(index, 0, sizeof(int)); 425 | memcpy(index, &i, sizeof(int)); 426 | 427 | ntyThreadPoolQueue(pool, king_counter, index); 428 | 429 | } 430 | 431 | getchar(); 432 | printf("You are very good !!!!\n"); 433 | } 434 | 435 | 436 | 437 | 438 | -------------------------------------------------------------------------------- /thread_pool_simple.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * author : wangbojing 4 | * email : 1989wangbojing@163.com 5 | * github : https://github.com/wangbojing 6 | */ 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | 16 | 17 | #define LL_ADD(item, list) do { \ 18 | item->prev = NULL; \ 19 | item->next = list; \ 20 | list = item; \ 21 | } while(0) 22 | 23 | #define LL_REMOVE(item, list) do { \ 24 | if (item->prev != NULL) item->prev->next = item->next; \ 25 | if (item->next != NULL) item->next->prev = item->prev; \ 26 | if (list == item) list = item->next; \ 27 | item->prev = item->next = NULL; \ 28 | } while(0) 29 | 30 | 31 | typedef struct NWORKER { 32 | pthread_t thread; 33 | int terminate; 34 | struct NWORKQUEUE *workqueue; 35 | struct NWORKER *prev; 36 | struct NWORKER *next; 37 | } nWorker; 38 | 39 | typedef struct NJOB { 40 | void (*job_function)(struct NJOB *job); 41 | void *user_data; 42 | struct NJOB *prev; 43 | struct NJOB *next; 44 | } nJob; 45 | 46 | typedef struct NWORKQUEUE { 47 | struct NWORKER *workers; 48 | struct NJOB *waiting_jobs; 49 | pthread_mutex_t jobs_mtx; 50 | pthread_cond_t jobs_cond; 51 | } nWorkQueue; 52 | 53 | typedef nWorkQueue nThreadPool; 54 | 55 | static void *ntyWorkerThread(void *ptr) { 56 | nWorker *worker = (nWorker*)ptr; 57 | 58 | while (1) { 59 | pthread_mutex_lock(&worker->workqueue->jobs_mtx); 60 | 61 | while (worker->workqueue->waiting_jobs == NULL) { 62 | if (worker->terminate) break; 63 | pthread_cond_wait(&worker->workqueue->jobs_cond, &worker->workqueue->jobs_mtx); 64 | } 65 | 66 | if (worker->terminate) { 67 | pthread_mutex_unlock(&worker->workqueue->jobs_mtx); 68 | break; 69 | } 70 | 71 | nJob *job = worker->workqueue->waiting_jobs; 72 | if (job != NULL) { 73 | LL_REMOVE(job, worker->workqueue->waiting_jobs); 74 | } 75 | 76 | pthread_mutex_unlock(&worker->workqueue->jobs_mtx); 77 | 78 | if (job == NULL) continue; 79 | 80 | job->job_function(job); 81 | } 82 | 83 | free(worker); 84 | pthread_exit(NULL); 85 | } 86 | 87 | 88 | 89 | int ntyThreadPoolCreate(nThreadPool *workqueue, int numWorkers) { 90 | 91 | if (numWorkers < 1) numWorkers = 1; 92 | memset(workqueue, 0, sizeof(nThreadPool)); 93 | 94 | pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER; 95 | memcpy(&workqueue->jobs_cond, &blank_cond, sizeof(workqueue->jobs_cond)); 96 | 97 | pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER; 98 | memcpy(&workqueue->jobs_mtx, &blank_mutex, sizeof(workqueue->jobs_mtx)); 99 | 100 | int i = 0; 101 | for (i = 0;i < numWorkers;i ++) { 102 | nWorker *worker = (nWorker*)malloc(sizeof(nWorker)); 103 | if (worker == NULL) { 104 | perror("malloc"); 105 | return 1; 106 | } 107 | 108 | memset(worker, 0, sizeof(nWorker)); 109 | worker->workqueue = workqueue; 110 | 111 | int ret = pthread_create(&worker->thread, NULL, ntyWorkerThread, (void *)worker); 112 | if (ret) { 113 | 114 | perror("pthread_create"); 115 | free(worker); 116 | 117 | return 1; 118 | } 119 | 120 | LL_ADD(worker, worker->workqueue->workers); 121 | } 122 | 123 | return 0; 124 | } 125 | 126 | 127 | void ntyThreadPoolShutdown(nThreadPool *workqueue) { 128 | nWorker *worker = NULL; 129 | 130 | for (worker = workqueue->workers;worker != NULL;worker = worker->next) { 131 | worker->terminate = 1; 132 | } 133 | 134 | pthread_mutex_lock(&workqueue->jobs_mtx); 135 | 136 | workqueue->workers = NULL; 137 | workqueue->waiting_jobs = NULL; 138 | 139 | pthread_cond_broadcast(&workqueue->jobs_cond); 140 | 141 | pthread_mutex_unlock(&workqueue->jobs_mtx); 142 | 143 | } 144 | 145 | void ntyThreadPoolQueue(nThreadPool *workqueue, nJob *job) { 146 | 147 | pthread_mutex_lock(&workqueue->jobs_mtx); 148 | 149 | LL_ADD(job, workqueue->waiting_jobs); 150 | 151 | pthread_cond_signal(&workqueue->jobs_cond); 152 | pthread_mutex_unlock(&workqueue->jobs_mtx); 153 | 154 | } 155 | 156 | 157 | 158 | 159 | /************************** debug thread pool **************************/ 160 | 161 | #define KING_MAX_THREAD 80 162 | #define KING_COUNTER_SIZE 1000 163 | 164 | void king_counter(nJob *job) { 165 | 166 | int index = *(int*)job->user_data; 167 | 168 | printf("index : %d, selfid : %lu\n", index, pthread_self()); 169 | 170 | free(job->user_data); 171 | free(job); 172 | } 173 | 174 | 175 | 176 | int main(int argc, char *argv[]) { 177 | 178 | nThreadPool pool; 179 | 180 | ntyThreadPoolCreate(&pool, KING_MAX_THREAD); 181 | 182 | int i = 0; 183 | for (i = 0;i < KING_COUNTER_SIZE;i ++) { 184 | nJob *job = (nJob*)malloc(sizeof(nJob)); 185 | if (job == NULL) { 186 | perror("malloc"); 187 | exit(1); 188 | } 189 | 190 | job->job_function = king_counter; 191 | job->user_data = malloc(sizeof(int)); 192 | *(int*)job->user_data = i; 193 | 194 | ntyThreadPoolQueue(&pool, job); 195 | 196 | } 197 | 198 | getchar(); 199 | printf("\n"); 200 | 201 | 202 | } 203 | 204 | 205 | 206 | --------------------------------------------------------------------------------