从零编写c++之http服务器(1)-线程池

引言

        编写这么一个c++的http服务器,纯粹是满足多年前一个好奇心。为什么我输入链接可以打开一个网页?这背后究竟发生了什么?所以本着好事多磨的心理一点点从零写了这个http服务器。他有着以下几个特点。

1) 基于epoll的异步事件驱动框架

2) L/F线程池处理网络事件

3) 完全从零编写http服务模块

        当然实现中为了研究原理,仅做了关键功能的开发,也有诸多不足之处。本着分享共同进步的心,希望看官不惜赐教。

完整源码见<https://github.com/kwansoner/panda.git>

线程池

     线程池就是分配若干线程来复用处理任务,避免了不断创建销毁线程带来的重复工作,也避免了为每个连接创建一个线程的服务器设计。让我们献上类图。

                                          

        可以看到我们有一个线程池基类IThreadPool。定义了一个往线程池投入任务的方法pushtask。这个任务对象可以是继承自IThreadHandle的任何子类实例。线程池执行任务时调用IThreadHandle的threadhandle函数。

class IThreadHandle
{
    friend class CThreadPool;
    public:
        virtual ~IThreadHandle(){};
        
    protected:
        // desc: 线程池任务执行函数
        // param: void
        // return: void
        virtual void threadhandle() = 0;
};
 
class IThreadPool
{
    public:
        virtual ~IThreadPool(){};
 
        // desc: 往线程池放入任务
        // param: handle/执行任务对象 block/是否阻塞的方式放入
        // return: 0/成功返回 -1/错误
        virtual int pushtask(IThreadHandle *handle, bool block = true) = 0;
};
        首先是在构造函数中创建一定数量的线程,我们保存每个创建的线程的tid到vector中管理。在析构函数中进行销毁,向每个创建的线程发送cancel信号,然后等待线程退出。值得注意的是线程收到cancel信号时会在遇到取消点时退出。退出时会调用线程执行体中变量的析构函数。或者调用线程清理函数(如果安装了的话)。

void CThreadPool::create_threadpool()
{
    pthread_attr_t thread_attr;
    pthread_attr_init(&thread_attr);
    for(size_t i = 0; i < m_threadnum; i++){
        pthread_t tid = 0;
        if(pthread_create(&tid, &thread_attr, process_task, (void *)this) < 0){
            errsys("create thread[%d] filed\n", (int)i);
            continue;
        }
 
        m_thread.push_back(tid);
    }
    pthread_attr_destroy(&thread_attr);
    trace("create thread pool, thread number %d\n", (int)m_thread.size());
}
void CThreadPool::destroy_threadpool()
{
    void *retval = NULL;
    vector_tid_t::iterator itor = m_thread.begin();
    for(; itor != m_thread.end(); itor++){
        if(pthread_cancel(*itor) < 0 || pthread_join(*itor, &retval) < 0){
            errsys("destroy thread[%d]\n", (int)(*itor));
            continue;
        }
    }
 
    m_thread.clear();
    trace("destroy thread pool... done\n");
}
        接下来我们看线程执行体,首先选出一个线程作为Leader,成为Leader后该线程设置一下标志m_hasleader。值得注意的是这里对于条件变量返回时的判断m_hasleader标志是while循环,为什么不是if判断就好了呢?那是因为条件变量存在多次返回的可能,在man手册中注意事项有解释。

        当一个线程成为Leader后从队列m_taskqueue中获取任务,等取出一个任务后调用join_follwer取消m_hasleader标志,并通知其他线程可以成为Leader。然后就是去处理该任务,也就是回调对象的threadhandle函数。

void CThreadPool::promote_leader()
{
    Pthread::CGuard guard(m_identify_mutex);
    while(m_hasleader){    // more than one thread can return
        m_befollower_cond.wait(m_identify_mutex);
    }
    m_hasleader = true;
}
void CThreadPool::join_follwer()
{
    Pthread::CGuard guard(m_identify_mutex);
    m_hasleader = false;
    m_befollower_cond.signal();
}
void *CThreadPool::process_task(void * arg)
{
    CThreadPool &threadpool = *(CThreadPool *)arg;
    while(true){
        threadpool.promote_leader();    
        IThreadHandle *threadhandle = NULL;
        int ret = threadpool.m_taskqueue.pop(threadhandle);
        threadpool.join_follwer();        
 
        if(ret == 0 && threadhandle)
            threadhandle->threadhandle();
    }    
 
    pthread_exit(NULL);
}
--------------------- 
作者:kwanson 
来源:CSDN 
原文:https://blog.csdn.net/kwanson/article/details/81160437 
版权声明:本文为博主原创文章,转载请附上博文链接!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值