Linux C 实现线程池
来源:互联网 发布:前海人寿校招 知乎 编辑:程序博客网 时间:2024/04/29 09:23
最近做的一些工作需要用到线程池技术,因此参考了一些资料和书籍,如《0bug c/c++商用工程之道》。
为此在linux平台上用纯c写了一个线程池的实现。
在此列出了原代码。
主要用到的数据结构有
1.struct thread_pool_t // thread pool 的实现代码
2.struct thread_pool_token_t //在thread pool 中用到的结构,一个该类型变量代表一条线程
3.struct thread_pool_job_t //当线前程处理该任务将回调结构
设计思路:
主线程创建线程池的控制线程,
管理线程将进入循环来做以下事情:
1.如果当前空闲进程不足并且总线程数没有达到上限,将创建一条空闲线程,至道空闲线程达到3条(见THREAD_POOL_MIN_IDLE_SIZE)
2.不断检查工作任务队列,有则安排一条空闲线程对该任务进行处理
普通线程被管理线程创建后也将进入循环来做以下事情:
1.检查当前assign的thread_pool_token_t结构的状态,如果是TREAD_POOL_STATE_BUSY则调用thread_pool_token_t所注册的回调函数来处理任务。
2.完成该任务后检测当前空闲进程够不够,如果太多则退出本线程,否则会改写运行状态为TREAD_POOL_STATE_IDLE后继续循环
注册任务:
1.当没有空闲线程可用时将把该任务加入工作任务队列(需定义_THREAD_POOL_JOB_LIST_),以备将来有空闲线程是能够进行处理。
主要文件 :
1.thread_pool.h
#ifndef _LIB_DOL_THREAD_POOL_H_
#define _LIB_DOL_THREAD_POOL_H_
#include "thread_pool.def"
#include <pthread.h>
#define THREAD_POOL_MIN_SIZE 5 /* at least 5 threads, 4 job + 1 manage thread */
#define THREAD_POOL_MIN_IDLE_SIZE 3 /* at least 3 threads idle to improve the efficiency */
#define THREAD_POOL_DEFAULT_SIZE 10 /* default threads in thread pool */
#define THREAD_POOL_MAX_SIZE 100 /* max threads in thread pool */
#define TREAD_POOL_STATE_NOTRUN 0
#define TREAD_POOL_STATE_IDLE 1
#define TREAD_POOL_STATE_BUSY 2
#define TREAD_POOL_REG_OK 0
#define TREAD_POOL_REG_ERR -1
#define TREAD_POOL_REG_ERR_FULL -2
#define ETPNOUSE -2 /* thread pool can not use */
#define ETPERR -1 /* thread pool over flow */
#define ETPOK 0 /* thread pool register ok */
#define CREATE_THREAD_SLEEP ( 200 * 1000 ) //200ms
#define LOOP_SLEEP ( 150 * 1000 ) //150ms
typedef void (*job_call_back_t)(void * );
typedef struct _thread_pool_job{
/* call back function which the thread will call to process this job */
job_call_back_t call_back;
/* parameter for call back function */
void * param;
/* job desc */
char desc[64];
#ifdef _THREAD_POOL_JOB_LIST_
/* if too many job then we will use this list to store the other job */
struct _thread_pool_job* next;
#endif
}thread_pool_job_t;
struct _thread_pool;
typedef struct _thread_pool_token{
int token_id;
int state;
int total_job; /* total jobs */
pthread_t thread_id;
thread_pool_job_t* job;
struct _thread_pool* pool;
pthread_mutex_t lock;
} thread_pool_token_t;
typedef struct _thread_pool{
int size; /* thread pool size */
int active; /* total create thread count */
int busy; /* total busy thread count */
int idle; /* total idle thread count */
int quit; /* should we quit the thread 1-quit, 0-loop */
int total_job; /* total process register job */
int reg_err; /* total register err */
#ifdef _THREAD_POOL_JOB_LIST_
/* use a link list to buff job if too many jobs are registered */
thread_pool_job_t* job_head;
/* lock for manuplate job list */
pthread_mutex_t job_lock;
#endif
/* lock */
pthread_mutex_t pool_lock;
/* thread pool core data */
thread_pool_token_t token[THREAD_POOL_MAX_SIZE];
}thread_pool_t;
/*
* init a thread pool with pool_size threads
* if pool_size is 0 or less than THREAD_POOL_IDLE_SIZE then pool_size set to THREAD_POOL_DEFAULT_SIZE
* if pool_size is larger than THREAD_POOL_MAX_SIZE then pool_size set to THREAD_POOL_MAX_SIZE
* return 0 if ok, -1 if error
*/
int thread_pool_init(thread_pool_t * pool, int pool_size);
/*
* thread pool destroy
*/
void thread_pool_destroy(thread_pool_t * pool);
/*
* pool - the thread pool
* call_back - call back function for thread token to call
* param - call back function parameter
* desc - tsk description
* it's thread safe when multi thread register thread pool job
*/
int thread_pool_register_job(thread_pool_t * pool, job_call_back_t call_back, void * param, char* desc);
/*
* out put the pool statistic data to stdout
*/
void thread_pool_statistic(thread_pool_t * pool);
/*
* out put the pool run time data to stdout
*/
void thread_pool_runtime(thread_pool_t * pool);
/*
* change the default log file to log_file
*/
void thread_pool_set_log(char* log_file);
/* thread pool token thread serve function */
static void * thread_pool_serve(void * arg);
/* thread pool control thread function */
static void * thread_pool_manage(void * arg);
/* destroy the mutex lock */
static void thread_pool_clean_up_lock(thread_pool_t * pool);
/* thread pool log */
static void thread_pool_log(char* fmt, ...);
/*
* find a not use token
* -1 if not found , else the token index
*/
static int thread_pool_find_free_token(thread_pool_t * pool);
/*
* find a runing and idle token
* -1 if not found , else the token index
*/
static int thread_pool_find_serve_token(thread_pool_t * pool);
#ifdef _DEBUG_VERSION_
#define _DOL_FPRINTF_ fprintf
#else
#define _DOL_FPRINTF_ //fprintf
#endif
#ifdef _THREAD_POOL_JOB_LIST_
/* pop a job from job list */
static thread_pool_job_t* pop_job(thread_pool_t * pool);
/* push a job in job list */
static void push_job(thread_pool_t * pool, thread_pool_job_t* job);
/* destroy the job list */
static void destroy_job_list(thread_pool_job_t* job_head);
#endif
#endif // end if define _LIB_DOL_THREAD_POOL_H_
2. thread_pool.c
//COPYRIGHT AND PERMISSION NOTICE //Copyright (c) 2010, Dolphin Cheung, <dolphin98629@163.com>. //All rights reserved. //Permission to use, copy, modify, and distribute this software for any purpose//with or without fee is hereby granted, provided that the above copyright//notice and this permission notice appear in all copies. //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN//NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,//DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR//OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE//OR OTHER DEALINGS IN THE SOFTWARE. //Except as contained in this notice, the name of a copyright holder shall not//be used in advertising or otherwise to promote the sale, use or other dealings//in this Software without prior written authorization of the copyright holder.
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <pthread.h>#include <errno.h>#include <time.h>#include <stdarg.h>#include <string.h>#include "thread_pool.h"static const char * default_log_file = "/var/log/libdol_tp.log";static char real_log_file[256];/* thread pool token thread serve function */static void * thread_pool_serve(void * arg){ thread_pool_token_t* token = (thread_pool_token_t*) arg; int self_quit = 0; token->state = TREAD_POOL_STATE_IDLE; pthread_mutex_lock(&(token->pool->pool_lock)); token->pool->idle ++; token->pool->active++; pthread_mutex_unlock(&(token->pool->pool_lock)); usleep(LOOP_SLEEP); thread_pool_log("serve thread create: token:%d thread:%u pid:%d ppid:%d",token->token_id, token->thread_id,(int)getpid(),(int)getppid()); while(! token->pool->quit){ /* do we have job ? */ if(token->state == TREAD_POOL_STATE_BUSY && token->job && token->job->call_back){ /* modify pool stat. */ pthread_mutex_lock(&(token->pool->pool_lock)); token->pool->idle --; token->pool->busy ++; token->pool->total_job ++; pthread_mutex_unlock(&(token->pool->pool_lock)); /* * process this job; this job may be a daemon process * so it may process all the time, you should design carefully * when it's time to quit in the while loop for the daemon process * the thread pool have no way to tell the daemon process we will quit * so when quit the thread pool, you also need to tell your * daemon process to quit. */ token->job->call_back(token->job->param); pthread_mutex_lock(&(token->lock)); token->total_job ++; free(token->job); token->job = NULL; token->state = TREAD_POOL_STATE_IDLE; pthread_mutex_unlock(&(token->lock)); /* modify pool stat. */ pthread_mutex_lock(&(token->pool->pool_lock)); token->pool->idle ++; token->pool->busy --; /* too many idle thread ? */ if(token->pool->idle > THREAD_POOL_MIN_IDLE_SIZE){ self_quit = 1; } pthread_mutex_unlock(&(token->pool->pool_lock)); } if(self_quit){ break; } usleep(LOOP_SLEEP); } thread_pool_log("serve thread quit: token:%d thread:%u job:%d pid:%d ppid:%d", token->token_id, token->thread_id,token->total_job,(int)getpid(),(int)getppid()); //thread quit pthread_mutex_lock(&(token->lock)); token->state = TREAD_POOL_STATE_NOTRUN; token->thread_id = 0; pthread_mutex_unlock(&(token->lock)); pthread_mutex_lock(&(token->pool->pool_lock)); token->pool->idle --; token->pool->active --; pthread_mutex_unlock(&(token->pool->pool_lock)); return NULL;}/* thread pool control thread function */static void * thread_pool_manage(void * arg){ thread_pool_t *pool = (thread_pool_t*) arg; thread_pool_job_t* job_node; int i, rc , idle, active,busy; if(pool == NULL){ thread_pool_log("WARNING: thread_pool_manage stop when pool is NULL"); return NULL; } pool->active++; usleep(CREATE_THREAD_SLEEP ); thread_pool_log("manage thread create. pid:%d ppid:%d",(int)getpid(),(int)getppid()); while(! pool->quit){ pthread_mutex_lock(&(pool->pool_lock)); idle = pool->idle; active = pool->active; busy = pool->busy; pthread_mutex_unlock(&(pool->pool_lock)); if( idle < THREAD_POOL_MIN_IDLE_SIZE){ //less than preset idle thread then create new therad if(active < pool->size){ //ok we can create new thread now i = thread_pool_find_free_token(pool); if(i == -1){ //sorry , it's unusual case _DOL_FPRINTF_(stderr,"WARNING: can not found free token/n"); thread_pool_log("WARNING: can not found free token when create new thread"); }else{ rc = pthread_create(&(pool->token[i].thread_id), NULL, &thread_pool_serve, &(pool->token[i])); if(rc !=0 ){ fprintf(stderr,"ERROR: thread pool create serve thread fail! code[%d] desc[%s]/n",errno,strerror(errno)); thread_pool_log("ERROR: thread pool create serve thread fail! code[%d] desc[%s]/n",errno,strerror(errno)); pthread_mutex_lock(&(pool->token[i].lock)); pool->token[i].state = TREAD_POOL_STATE_NOTRUN; pthread_mutex_unlock(&(pool->token[i].lock)); } usleep(CREATE_THREAD_SLEEP ); } }else{ //sorry we're too busy _DOL_FPRINTF_(stdout,"WARNING: no more idle thread, total:%d idle:%d busy:%d nrun:%d/n",active,idle,busy,active-idle-busy-1); thread_pool_log("WARNING: we're busy, total:%d idle:%d busy:%d nrun:%d",active,idle,busy,active-idle-busy-1); } }#ifdef _THREAD_POOL_JOB_LIST_ while(job_node = pop_job(pool)){ i = thread_pool_find_serve_token(pool); if(i == -1){ /* we will break the loop if no more serve token found and push back the job */ push_job(pool,job_node); break; }else{ pthread_mutex_lock(&(pool->token[i].lock)); /* make the register thread safe */ if(pool->token[i].state == TREAD_POOL_STATE_IDLE){ pool->token[i].job = job_node; /* set thread state to busy and the thread will process the job */ pool->token[i].state = TREAD_POOL_STATE_BUSY; }else{ push_job(pool,job_node); } pthread_mutex_unlock(&(pool->token[i].lock)); } }#endif usleep(LOOP_SLEEP ); } /* wait for other serve thread to terminal */ while(pool->active > 1){ usleep(LOOP_SLEEP ); } thread_pool_clean_up_lock(pool); thread_pool_log("thread pool manage thread terminal! pid:%d ppid:%d",(int)getpid(),(int)getppid()); pool->active --; return NULL;}void thread_pool_set_log(char* log_file){ if(log_file == NULL) return; if(strlen(log_file) > sizeof(real_log_file)) return; strcpy(real_log_file,log_file);}/* thread pool log */static void thread_pool_log(char* fmt, ...){ va_list args; FILE * fo; char date[32], ttime[32], filename[128], fpath[128]; char buffer[1024]; time_t tnow; struct tm * tm_time; time(&tnow); tm_time = localtime(&tnow); strftime(date, sizeof(date), "%y%m%d", tm_time); strftime(ttime, sizeof(ttime), "%H%M%S", tm_time); sprintf(filename, "%s", real_log_file); //if(access(filename, F_OK) != 0){ //mkdir(fpath, 00777); //chmod(fpath, 00777); //} //chmod(filename, 00666); if((fo = fopen(filename, "a")) == 0){ fprintf(stderr,"cannot open %s", fo); return; } va_start(args,fmt); vsprintf(buffer,fmt,args); fprintf(fo,"%s %s/n",ttime,buffer); va_end(args); fclose(fo);}int thread_pool_init(thread_pool_t * pool, int pool_size){ int rc,i; pthread_t tid; if(pool == NULL){ return ETPNOUSE; } memset(pool,0x00,sizeof(thread_pool_t)); memset(real_log_file,0x00,sizeof(real_log_file)); sprintf(real_log_file,"%s",default_log_file); /* at least */ if(pool_size < THREAD_POOL_MIN_SIZE) pool_size = THREAD_POOL_DEFAULT_SIZE; else if(pool_size >= THREAD_POOL_MAX_SIZE ) pool_size = THREAD_POOL_MAX_SIZE; pool->size = pool_size; thread_pool_log("thread pool init with size:%d",pool->size); pthread_mutex_init(&(pool->pool_lock),NULL);#ifdef _THREAD_POOL_JOB_LIST_ pthread_mutex_init(&(pool->job_lock),NULL); pool->job_head = NULL;#endif for(i=0;i<pool_size;i++){ pthread_mutex_init(&(pool->token[i].lock),NULL); pool->token[i].token_id = i; pool->token[i].state = TREAD_POOL_STATE_NOTRUN; pool->token[i].job = NULL; pool->token[i].pool = pool; } /* only create the manager thread, and it will create other serve threads */ rc = pthread_create(&tid, NULL, &thread_pool_manage, pool); if(rc !=0 ){ fprintf(stderr,"ERROR:thread pool create fail! code[%d] desc[%s]/n",errno,strerror(errno)); thread_pool_log("ERROR:thread pool create fail! code[%d] desc[%s]/n",errno,strerror(errno)); pthread_mutex_destroy(&(pool->pool_lock));#ifdef _THREAD_POOL_JOB_LIST_ pthread_mutex_destroy(&(pool->job_lock));#endif for(i=0;i<pool_size;i++){ pthread_mutex_destroy(&(pool->token[i].lock));; } return ETPERR; }else{ _DOL_FPRINTF_(stdout,"thread pool create manage thread %u completed/n",tid); } usleep(CREATE_THREAD_SLEEP); return ETPOK;}void thread_pool_destroy(thread_pool_t * pool){ if(pool == NULL) return; pool->quit = 1; while(pool->active){ usleep(LOOP_SLEEP ); _DOL_FPRINTF_(stdout,"Still have %d threads in thread pool/n",pool->active); }#ifdef _THREAD_POOL_JOB_LIST_ destroy_job_list(pool->job_head);#endif thread_pool_log("thread pool destroy! terminal %d threads; total %d jobs",pool->size, pool->total_job);}int thread_pool_register_job(thread_pool_t * pool, job_call_back_t call_back, void * param, char* desc ){ int i=0,rc; thread_pool_job_t* job = (thread_pool_job_t*)malloc(sizeof(thread_pool_job_t)); if(job == NULL){ /* no more memory */ return TREAD_POOL_REG_ERR; } memset(job,0x00,sizeof(thread_pool_job_t)); job->call_back = call_back; job->param = param; if(desc) sprintf(job->desc,"%s",desc); else sprintf(job->desc,"N/A"); i = thread_pool_find_serve_token(pool); if(i == -1){#ifdef _THREAD_POOL_JOB_LIST_ job->next = NULL; push_job(pool,job); return TREAD_POOL_REG_OK;#else if(job) free(job); pool->reg_err ++; return TREAD_POOL_REG_ERR_FULL;#endif } pthread_mutex_lock(&(pool->token[i].lock)); /* make the register thread safe */ if(pool->token[i].state == TREAD_POOL_STATE_IDLE){ pool->token[i].job = job; /* set thread state to busy and the thread will process the job */ pool->token[i].state = TREAD_POOL_STATE_BUSY; rc = TREAD_POOL_REG_OK; }else{ pool->reg_err ++; rc = TREAD_POOL_REG_ERR; } pthread_mutex_unlock(&(pool->token[i].lock)); return rc;}static void thread_pool_clean_up_lock(thread_pool_t * pool){ int i; if(pool == NULL) return; for(i=0; i<pool->size; i++){ pthread_mutex_destroy(&(pool->token[i].lock)); } pthread_mutex_destroy(&(pool->pool_lock));#ifdef _THREAD_POOL_JOB_LIST_ pthread_mutex_destroy(&(pool->job_lock));#endif}void thread_pool_statistic(thread_pool_t * pool){ if(pool == NULL){ _DOL_FPRINTF_(stderr,"thread_pool_statistic(NULL)/n"); return; } fprintf(stdout,"/n *** thread pool statistic ***/n"); fprintf(stdout," size:%d/t job:%d/t reg_err:%d/n active:%d/t busy:%d/t idle:%d/t/n" ,pool->size,pool->total_job,pool->reg_err,pool->active,pool->busy,pool->idle);}void thread_pool_runtime(thread_pool_t * pool){ int i; thread_pool_job_t* job; char state[16]; if(pool == NULL){ _DOL_FPRINTF_(stderr,"thread_pool_runtime(NULL)/n"); return; } pthread_mutex_lock(&(pool->pool_lock)); fprintf(stdout,"/n *** thread pool runtime ***"); fprintf(stdout,"/n size:%d/t job:%d/t reg_err:%d/n active:%d/t busy:%d/t idle:%d/t/n" ,pool->size,pool->total_job,pool->reg_err,pool->active,pool->busy,pool->idle); pthread_mutex_unlock(&(pool->pool_lock)); fprintf(stdout,"/n id/t tid/t job/t stat/t desc/n"); for(i=0; i<pool->size; i++){ pthread_mutex_lock(&(pool->token[i].lock)); if(pool->token[i].state != TREAD_POOL_STATE_NOTRUN){ fprintf(stdout, " %d/t %u/t %d/t ", pool->token[i].token_id,pool->token[i].thread_id,pool->token[i].total_job); if(pool->token[i].state == TREAD_POOL_STATE_NOTRUN){ sprintf(state,"NOTRUN"); }else if(pool->token[i].state == TREAD_POOL_STATE_IDLE){ sprintf(state,"IDLE"); }else if(pool->token[i].state == TREAD_POOL_STATE_BUSY){ sprintf(state,"BUSY"); }else{ sprintf(state,"UNKNW"); } if(pool->token[i].job){ fprintf(stdout, "%s/t %s/n",state,(pool->token[i].job)->desc); }else{ fprintf(stdout, "%s/t N/A/n",state); } } pthread_mutex_unlock(&(pool->token[i].lock)); }#ifdef _THREAD_POOL_JOB_LIST_ pthread_mutex_lock(&(pool->job_lock)); job = pool->job_head; while(job){ fprintf(stdout," N/A/t N/A/t N/A/t STDBY/t %s/n",job->desc); job = job->next; } pthread_mutex_unlock(&(pool->job_lock));#endif}static int thread_pool_find_free_token(thread_pool_t * pool){ int idx = -1, i; if(pool == NULL) return -1; for(i=0; i < pool->size; i++ ){ pthread_mutex_lock(&(pool->token[i].lock)); if(pool->token[i].state == TREAD_POOL_STATE_NOTRUN){ idx = i; } pthread_mutex_unlock(&(pool->token[i].lock)); if(idx != -1) break; } return idx;}static int thread_pool_find_serve_token(thread_pool_t * pool){ int idx = -1, i = 0; if(pool == NULL) return -1; for(i=0; i<pool->size; i++ ){ pthread_mutex_lock(&(pool->token[i].lock)); if(pool->token[i].state == TREAD_POOL_STATE_IDLE){ idx = i; } pthread_mutex_unlock(&(pool->token[i].lock)); if(idx != -1) break; } return idx;}#ifdef _THREAD_POOL_JOB_LIST_static thread_pool_job_t* pop_job(thread_pool_t * pool){ thread_pool_job_t* job_node = NULL; if(pool){ pthread_mutex_lock(&(pool->job_lock)); job_node = pool->job_head; if(job_node && job_node->next){ pool->job_head = job_node->next; job_node->next = NULL; }else{ pool->job_head = NULL; } pthread_mutex_unlock(&(pool->job_lock)); } return job_node;}static void push_job(thread_pool_t * pool, thread_pool_job_t* job){ if(job == NULL) return; if(pool){ pthread_mutex_lock(&(pool->job_lock)); job->next = pool->job_head; pool->job_head = job; pthread_mutex_unlock(&(pool->job_lock)); } return;}static void destroy_job_list(thread_pool_job_t* job_head){ thread_pool_job_t* job_node ; while(job_head){ job_node = job_head; job_head = job_head->next; free(job_node); } job_head = NULL; return;}#endif
3. thread_pool.def
#ifndef _THREAD_POOL_DEF_#define _THREAD_POOL_DEF_#ifndef _THREAD_POOL_JOB_LIST_#define _THREAD_POOL_JOB_LIST_#endif//#ifndef _DEBUG_VERSION_//#define _DEBUG_VERSION_//#endif#endif //end define _THREAD_POOL_DEF_
4.Makefile
obj=thread_pool.o
src=thread_pool.c
inc=thread_pool.h
def=thread_pool.def
target=$(obj)
.PHONY: all
.PHONY: clean
.PHONY: lib
all: $(target)
@echo "Building Dolphin's Libaray"
$(obj): $(src) $(inc) $(def)
gcc -g -c -fPIC $< -o $@
clean:
rm -f *.o *.so *.a
lib: $(target)
gcc -shared -fPIC -o libdol.so $(target)
ar cr libdol.a $(target)
以上便是主要的源代码。
下面给出一个使用该线程池的例子:
#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<signal.h>#include "thread_pool.h"int quit =0;void sig_handler(int sig_no){ quit = 1;}void call_back_func(void * arg ){ int p = (int)(void*)arg; int i=0; while(!quit){ //fprintf(stdout,"%-3d alive/n",p); sleep(1); break; }}int main(int argc, char** argv){ thread_pool_t pool; job_call_back_t call_back; int i,rc; char desc [64]; signal(SIGTERM, sig_handler); signal(SIGINT, sig_handler); rc = thread_pool_init(&pool, 5); thread_pool_set_log("./test_tp.log"); if(rc != ETPOK){ fprintf(stderr,"thread_pool_init fail/n"); return 1; } sleep(2); call_back = call_back_func; for(i=0;i<10;i++){ sprintf(desc,"job %d",i); //sleep(1); rc = thread_pool_register_job(&pool, call_back, (void*)i, desc); if(rc != TREAD_POOL_REG_OK){ fprintf(stderr,"regiser %d %s fail/n",i,desc); } } while(!quit){ thread_pool_runtime(&pool); sleep(1); } thread_pool_destroy(&pool); fprintf(stdout,"program stoping!/n"); return 0;}
Makefile:
exe=test_tp
src=test_tp.c
obj=test_tp.o
.PHONY: all
.PHONY: clean
all: $(exe)
clean:
rm -f $(exe) $(obj)
$(exe): $(obj)
gcc -g $^ -o $@ -lpthread -L.. -ldol
$(obj): $(src)
gcc -g -c $< -o $@ -I..
- Linux C 实现线程池
- linux C线程池实现
- linux c 实现线程池
- Linux线程池C语言实现
- linux线程池的C语言实现
- linux线程池的C语言实现
- linux线程池的C语言实现
- linux线程池的C语言实现
- Linux下C线程池的实现
- linux线程池的C语言实现
- 简单Linux C线程池的实现
- Linux C下实现线程池
- Linux下C线程池的实现
- Linux下C线程池的实现
- linux c线程池简单实现
- Linux下C线程池的实现
- Linux下C线程池的实现
- linux系统c线程池的实现
- 2010新概念
- 123
- 天眼学习版防火墙(个人版)
- log4net.Layout.XmlLayoutSchemaLog4j 中文字符问题
- 网页上加在线客服代码QQ,MSN,skype,goolge TALK,雅虎通,贸易通,淘宝旺旺
- Linux C 实现线程池
- 视图及视图类的简单说明
- Silverlight+IIS配置时遇到的几个错误及解决思路!
- Qt: Qt中动态链接库的使用
- USB枚举详细过程剖析
- [转]Android 应用初始化及窗体事件(按键)的分发 [此博文包含图片]
- Linux curl使用简单介绍
- svn客户端的配置
- 如何系统学习C++