面试必知-浅谈线程池及其实现(面向面试的理解)

上了一节关于线程池的课,根据老师和自己的理解记的笔记,能说出什么是线程池,它的实现的各个模块以及怎么工作的。

什么是线程池?
为什么有线程池?
什么是线程?什么是进程?

	线程是操作系统调度的基本单位

池:

    内存池,数据库连接池,请求池,消息队列

池起到了一个什么作用:

    池式结构起到缓冲区的作用

线程池为什么起到缓冲区的作用?
比较老的一个概念: 一请求一线程。

每来一个客户端请求,服务器分配一个线程,比如说同时来1W个请求,服务器能否承受住?为什么不能?是内存不够?比如:Linux Posix Thread 8M的空间,8M*1024 = 80G,即使分配虚拟空间也是很大的。
所以不能分配。

既然不能一次性分配N多线程,怎么办?有没有方法?
线程池:

	引入线程池: 
	                      1.我们不需要每一次都创建
				          2.使用完的时候,返回给线程池
				          3.异步解耦的作用。
				          (什么概念:什么是异步,就是它跟主体流程不在一个线程里边,通过另外一个线程走。
				          把任务丢给线程池,调度分配执行这个任务,主任务上边,
				          只需要把任务丢给线程池,起到异步作用,同步和异步之间的性能有10倍之差)

线程池怎么实现,用到自己的项目里边:

首先考虑的几点:1.如何从线程池里边拿一个线程
				2.如何把线程返回给线程池
				3.线程池里边的线程的数量的控制	

线程池,有现成的: Boost
Linux环境,实现一个线程池
面试中经常有

什么时候会用到线程池:

   1:在向服务器发送请求信息的时候,每一次写日志,
      抛到线程下h池里边,再有线程池写到磁盘上(落盘)
   2:读,解析。读数据与解析数据

线程池在用的时候有什么特点:

 1:准备好任务
 2:由线程池处理

大家口中的线程池:

     1:一次性创建多个线程
     2:每次使用的时候取一个
     3:把线程用完,把空闲线程返回
     4:异步解耦的作用(最重要的)

如何实现一个线程池:

在这里插入图片描述
类似食堂打饭,银行营业厅

在这里插入图片描述
a. 柜员–>线程,–>柜员队–>线程队列
b. 客户–>任务,–>客户队列–>任务队列
如何使得客户与柜员有秩序的工作?
防止两个客户跑到柜台,
两个柜员为一个客户服务。叫号机,也就是公示牌, 那么这个公示牌应该是谁的属性?都不是,是使得营业厅有秩序的工作的工具
c. 营业厅–>管理组件来管理线程与任务队列,有秩序
互斥锁来解决临界资源问题,条件变量

注意点:

1.线程同步问题,互斥锁,条件变量,信号量都可以用于线程同
步,单纯的互斥锁只能轮询去处理数据,可能会空转进而造成CPU
资源的浪费,信号量并不建议用(并发编程的十五条建议),所以使用
互斥锁+条件变量来解决线程同步的问题,可参考条件变量实现生产
者消费者模型

上述的总结就是:
实现

 1:先实现一个任务队列
2:线程的集合(也是用队列存,线程id)
3:管理组件(使线程有秩序的工作)
 (1)	互斥资源,加锁mutex
 (2)	任务队列里没有任务,线程怎么做,在休息,
        在等待某个条件,即队列中有任务,条件变量
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
//宏
#define LL_INSERT(item, list) do{ \
    //添加节点
    item->prev = NULL;\
    item->next = list;\
    list->prev = item;\
    list = item;\
}while(0)

#define LL_REMOVE(item, list) do{\
    if(item->prev != NULL) item->prev->next = item->next;\
    if(item->next != NULL) iten->next->prev = item->prev;\
    if(list == item) list = list->next;\//第一个节点
    item->prev = item->next = NULL;
}while(0)
typedef struct NWORKER{  //(线程|柜员)

    pthread_t thread;  //线程的id
     struct NWORKER *pool;
    //将线程串联起来
    struct NWORKER *prev;//前驱
    struct NWORKER *next;//后继


}nWorker;

typedef struct NJOB{ //(任务|客户)
    //用回调函数区别每一个任务
    void(*job_func)(struct NJOB *job);
    //不同的任务需要传输不同的数据
    void *user_data;

    //产生队列
    struct NJOB* prev;
    struct NJOB* next;
}nJob;

typedef struct  NTHREADPOOL{//(管理组件,))

    struct NWORKER *workers;
    struct NJOB* jobs;

    pthread_mutex_t mutex;
    pthread_cond_t cond;

}nThreadPool;



static void *worker_callback(void *arg){
//使用静态static为了防止和其他文件中的重名
    printf("threadid:0x%x, working on task %d\n", pthread_self(), *(int*)arg);
    sleep(1);
    return(NULL);
}
//func api


/*线程池初始化*/
int nThreadPoolCreate(nThreadPool *pool,int numWorkers)
{
    if(numWorkers < 1) numWokers = 1;//最少是1
    if(pool == NULL) assert(0);
    memset(pool,0,sizeof(nThreadPool));
    //初始化
    pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;
    memcpy(&pool->mutex,&blank_mutex,sizeof(pthrad_mutex_t));

    pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
    memcpy(&pool->cond,&blank_cond,sizeof(pthrad_cond_t)):
    /*创建工作线程*/
    int i = 0;
    for(i = 0; i < numWorkers; i++)
    {

        nWorker *worker =(nWorker)malloc(sizeof(nWorker));
        if(worker == NULL)
        {
            perror("malloc");
            return -1;
            //创建失败的话,把之前的也都释放
        }
        memset(worker,0,sizeof(nWoker));
        nWorker->pool = pool;

        //创建线程
        int ret = phread_create(worker->thread,NULL,worker_callback,(void)worker);
        if(ret)//判断一下类型
        {
            perror("pthread_create");
            free(worker);
            return 1;
        }

        LL_INSERT(worker, pool->workers); //添加节点

    }
/*工作线程函数*/
void* thread_routine(void* arg)
{

}



return 0;
}






上边这个代码还没有实现,下边这个可以运行

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <windows.h>

/* 任务,任务回调函数,任务队列 */
typedef struct worker {
    void* (*callback) (void* arg); /*任务回调函数*/
    void* arg;                     /*回调函数的参数*/
    struct worker* next;           /*任务队列链表*/
} CThread_worker;

/*回调函数*/
static void* callback(void* arg) {
    printf("threadid:0x%x, working on task %d\n", pthread_self(), *(int*)arg);
    Sleep(1);
    return(NULL);
}

/*线程池结构*/
typedef struct {
    pthread_mutex_t mutex;      /*互斥锁 */
    pthread_cond_t cond;        /*条件变量 */

    CThread_worker* queue_head; /*线程池的任务队列*/

    int shutdown;               /*是否摧毁线程池 0:不摧毁 1:摧毁 */
    pthread_t* threadid;        /*线程ID数组*/
    int max_thread_num;         /*线程池最大线程数*/
    int cur_queue_size;         /*任务队列在任务数目*/

} CThread_pool;

/*线程函数*/
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->mutex), NULL);
    pthread_cond_init(&(pool->cond), NULL);

    pool->queue_head = NULL;

    pool->shutdown = 0; /*0打开1关闭*/

    pool->cur_queue_size = 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* (*callback) (void* arg), void* arg) {
    /*构造一个新任务*/
    printf("pool add worker arg:%d\n", *(int*)arg);
    CThread_worker* newworker = (CThread_worker*) malloc(sizeof(CThread_worker));
    newworker->callback = callback;
    newworker->arg = arg;
    newworker->next = NULL; /*SET NULL*/

    pthread_mutex_lock(&(pool->mutex));

    /*将任务加入到任务队列中,也就是链表末端*/
    CThread_worker* worker = pool->queue_head;
    if (worker != NULL) {
        while (worker->next != NULL)
            worker = worker->next;
        worker->next = newworker;
    }
    else {
        pool->queue_head = newworker;
    }

  	/*是否需要唤醒线程*/
    int dosignal;
    if (pool->cur_queue_size == 0)
     	dosignal = 1;

    pool->cur_queue_size += 1; /*计数+1*/

    pthread_mutex_unlock(&(pool->mutex));

    /*需要叫醒工作线程*/
    if (dosignal)
    	pthread_cond_signal(&(pool->cond));

    return 0;
}

/*销毁线程池*/
int pool_destroy() {
	printf("pool destroy now\n");

	/*启用关闭开关*/
    if (pool->shutdown)
        return -1; /*防止两次调用*/
    pool->shutdown = 1;

    /*唤醒所有等待线程*/
    pthread_cond_broadcast(&(pool->cond));

    /*阻塞等待线程退出回收资源,还有另一种办法就是线程分离*/
    int i;
    for (i=0; i<pool->max_thread_num; ++i)
        pthread_join(pool->threadid[i], NULL);
    free(pool->threadid);
    pool->threadid = NULL;

    /*销毁任务队列*/
    CThread_worker* head = NULL;
    while (pool->queue_head != NULL) {
        head = pool->queue_head;
        pool->queue_head = pool->queue_head->next;
        free(head);
        head = NULL;
    }

    /*销毁互斥锁与条件变量*/
    pthread_mutex_destroy(&(pool->mutex));
    pthread_cond_destroy(&(pool->cond));

    free(pool);
    pool = NULL;
    printf("pool destroy end\n");
    return 0;
}

/*工作线程函数*/
void* thread_routine(void* arg) {
    printf("starting threadid:0x%x\n", pthread_self());

    for (; ;) {

        pthread_mutex_lock(&(pool->mutex));
        /*任务队列为空时wait唤醒,当销毁线程池时跳出循环*/
        while (pool->cur_queue_size == 0 && !pool->shutdown) {
            printf("threadid:0x%x is waiting\n", pthread_self());
            pthread_cond_wait(&(pool->cond), &(pool->mutex));
        }

        /*线程池要销毁了*/
        if (pool->shutdown) {
            pthread_mutex_unlock(&(pool->mutex));
            printf("threadid:0x%x will exit\n", pthread_self());
            pthread_exit(NULL);
        }

  		/*开始执行任务*/
        printf("threadid:0x%x is starting to work\n", pthread_self());

        /*等待队列长度减去1,并取出链表中的头元素*/
        pool->cur_queue_size -= 1;
        CThread_worker* worker = pool->queue_head;
        pool->queue_head = worker->next;
        pthread_mutex_unlock(&(pool->mutex));

        /*调用回调函数,执行任务*/
        (*(worker->callback)) (worker->arg);
        free(worker);
        worker = NULL;
    }
    return(NULL);
}


/*测试*/
int main(int argc, char const *argv[])
{
    pool_init(2); /*创建n个线程*/

    /*添加n个任务*/
    int* workingnum = (int*) malloc(sizeof(int) * 10); /* 一定要动态创建 */
    int i;
    for (i=0; i<5; ++i) {
    	workingnum[i] = i;
        pool_add_worker(callback, &workingnum[i]);
    }

    Sleep(5); /*等待所有任务完成*/

    pool_destroy(); /*销毁线程池*/
    free(workingnum);
    workingnum = NULL;
    return 0;
}

运行结果

在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值