Webserver学习笔记——基础知识&线程池

服务器开发的基本框架

  • I/O处理单元:处理客户端连接和网络数据的读写
  • 逻辑单元:负责业务逻辑的处理
  • 网络存储单元:与本地数据库和文件相关

事件处理模式

  • reactor 模式:主线程只监听文件描述符上是否有事件发生,若有则通知工作线程读写数据/接受新连接/处理客户端请求。通常由同步IO实现。
  • proactor 模式:主线程和内核负责接受新连接/读写数据等IO操作,工作线程只负责处理客户端请求。通常由异步IO实现。

使用同步IO模型模拟proactor模式的工作流程如下:

  1. 主线程往epoll内核事件表注册socket上的读就绪事件。

  2. 主线程调用epoll_wait等待socket上有数据可读

  3. 当socket上有数据可读,epoll_wait通知主线程,主线程从socket循环读取数据,直到没有更多数据可读,然后将读取到的数据封装成一个请求对象并插入请求队列。

  4. 睡眠在请求队列上某个工作线程被唤醒,它获得请求对象并处理客户请求,然后往epoll内核事件表中注册该socket上的写就绪事件

  5. 主线程调用epoll_wait等待socket可写。

  6. 当socket上有数据可写,epoll_wait通知主线程。主线程往socket上写入服务器处理客户请求的结果。

线程池

线程池通常与任务队列搭配使用。当任务队列有新任务到达时,唤醒某个工作线程,它取出队首任务并执行该任务。执行完毕后,该工作线程返回线程池,等待下一次的唤醒。

使用线程池的好处

  1. 降低资源消耗:线程池通过重用已存在的线程来执行任务,减少了因线程创建和销毁所带来的资源消耗。在并发场景下,频繁地创建和销毁线程会占用大量的系统资源,使用线程池则能有效降低这种消耗。
  2. 提高响应速度:当任务到达时,线程池能够立即分配空闲线程来执行任务,而无需等待新线程的创建。这可以显著减少任务的等待时间,提高系统的响应速度。

  3. 控制并发线程数量:在高并发场景下,过多的线程会导致系统资源的浪费和竞争,进而影响应用程序的性能。线程池允许开发人员通过配置核心线程数、最大线程数等参数来控制并发线程的数量,从而避免这种情况的发生。

 线程池的代码实现

template <typename T>
class threadpool
{
public:
    /*thread_number是线程池中线程的数量,max_requests是请求队列中最多允许的、等待处理的请求的数量*/
    threadpool(int actor_model, connection_pool *connPool, int thread_number = 8, int max_request = 10000);
    ~threadpool();
    bool append(T *request, int state);
    bool append_p(T *request);

private:
    /*工作线程运行的函数,它不断从工作队列中取出任务并执行之*/
    static void *worker(void *arg);
    void run();

private:
    int m_thread_number;        //线程池中的线程数
    int m_max_requests;         //请求队列中允许的最大请求数
    pthread_t *m_threads;       //描述线程池的数组,其大小为m_thread_number
    std::list<T *> m_workqueue; //请求队列
    locker m_queuelocker;       //保护请求队列的互斥锁
    sem m_queuestat;            //是否有任务需要处理
    connection_pool *m_connPool;  //数据库连接池
    int m_actor_model;          //模型切换
};
template <typename T>
threadpool<T>::threadpool( int actor_model, connection_pool *connPool, int thread_number, int max_requests) : m_actor_model(actor_model),m_thread_number(thread_number), m_max_requests(max_requests), m_threads(NULL),m_connPool(connPool)
{
    if (thread_number <= 0 || max_requests <= 0)
        throw std::exception();
    // 为线程数组分配空间
    m_threads = new pthread_t[m_thread_number];
    if (!m_threads)
        throw std::exception();
    // 创建thread_number个线程,并将它们设置为脱离线程
    for (int i = 0; i < thread_number; ++i)
    {
        if (pthread_create(m_threads + i, NULL, worker, this) != 0)
        {
            delete[] m_threads;
            throw std::exception();
        }
        if (pthread_detach(m_threads[i]))
        {
            delete[] m_threads;
            throw std::exception();
        }
    }
}
template <typename T>
threadpool<T>::~threadpool()
{
    delete[] m_threads;
}
// 向请求队列中添加请求
template <typename T>
bool threadpool<T>::append(T *request, int state)
{
    m_queuelocker.lock();
    // 如果请求队列的大小大于最大请求数,说明请求队列已满,解锁并返回false
    if (m_workqueue.size() >= m_max_requests)
    {
        m_queuelocker.unlock();
        return false;
    }
    // 将请求添加到请求队列中,解锁,然后信号量加1,唤醒一个线程
    request->m_state = state;
    m_workqueue.push_back(request);
    m_queuelocker.unlock();
    m_queuestat.post();
    return true;
}
// 向请求队列中添加请求
template <typename T>
bool threadpool<T>::append_p(T *request)
{
    m_queuelocker.lock();
    if (m_workqueue.size() >= m_max_requests)
    {
        m_queuelocker.unlock();
        return false;
    }
    m_workqueue.push_back(request);
    m_queuelocker.unlock();
    m_queuestat.post();
    return true;
}
// 工作线程运行的函数
template <typename T>
void *threadpool<T>::worker(void *arg)
{
    threadpool *pool = (threadpool *)arg;
    pool->run();
    return pool;
}
// 工作线程的处理逻辑
template <typename T>
void threadpool<T>::run()
{
    while (true)
    {
        // 等待信号量,即等待请求队列中有任务
        m_queuestat.wait();

        // 取出请求队列中的队首任务
        m_queuelocker.lock();
        if (m_workqueue.empty())
        {
            m_queuelocker.unlock();
            continue;
        }
        T *request = m_workqueue.front();
        m_workqueue.pop_front();
        m_queuelocker.unlock();

        if (!request)
            continue;
        
        // 如果反应堆模型为reactor, 工作线程读写网络数据,并处理客户端请求
        if (1 == m_actor_model)
        {
            // 如果需要读网络数据
            if (0 == request->m_state)
            {   
                // 如果读取数据成功,接下来处理客户端请求
                if (request->read_once())
                {
                    request->improv = 1;
                    connectionRAII mysqlcon(&request->mysql, m_connPool);
                    request->process();
                }
                // 读取数据失败,关闭连接
                else
                {
                    request->improv = 1;
                    request->timer_flag = 1;
                }
            }
            // 如果需要写网络数据
            else
            {
                // 如果写数据成功,接下来注册写事件
                if (request->write())
                {
                    request->improv = 1;
                }
                // 写数据失败,关闭连接
                else
                {
                    request->improv = 1;
                    request->timer_flag = 1;
                }
            }
        }
        // 如果反应堆模型为proactor,工作线程只处理客户端请求
        else
        {
            connectionRAII mysqlcon(&request->mysql, m_connPool);
            request->process();
        }
    }
}
#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值