C++模板类编译问题

错误事件

在做webserver项目时,将线程池的模板类的声明写在.h文件,代码如下:

#ifndef THREADPOOL_H
#define THREADPOOL_H
// 引入头文件
#include"../lock/locker.h"
#include<list>
#include<vector>
#include<iostream>
#include<exception>

template<typename T>
class threadPool{
public:
    
    threadPool(int thread_number = 8, int max_requests = 10000);
    // 需要注意的是,成员变量的默认值只能在声明里,而不能在定义中
    ~threadPool();
    bool append(T*);

private:
    int m_max_requets; // 请求队列中允许的最大请求数量
    int m_thread_number; // 线程池中线程数量
    std::list<T*> m_queue; // 任务队列
    pthread_t* m_threads; // 描述线程池的数组
    locker m_queueLocker; // 请求队列的锁
    sem m_queueStat;      // 是否有任务需要处理
    bool m_stop;            // 线程结束的标志
    // 如果写了数据库,线程池里还需要把数据库的成员变量加上去
    /* 线程工作运行的函数,该函数不断地从任务队列中取出任务并执行*/
    
    static void* work(void* args); 
    /* 静态函数没有this指针,只能操作静态变量 */
    void run();
};

#endif

将定义写在.cpp文件里,代码如下:

#include"threadPool.h"



template<typename T>
threadPool<T>::threadPool(int thread_number, int max_requests)
    : m_max_requets(max_requests),
      m_thread_number(thread_number),
      m_threads(nullptr),
      m_stop(false)
{
    if(m_max_requets <= 0 || m_thread_number <= 0){
        throw std::exception();
    }

    // 分配存放线程ID的内存空间    
    m_threads = new pthread_t[m_thread_number];
    if(!m_threads){ // 异常处理
        throw std::exception();
    }

    for(int i = 0; i < m_thread_number; ++ i){
        std::cout << "创建了第" << i << "th个线程" << std::endl;
        if(pthread_create(m_threads + i, nullptr, work, this) != 0){
            // 突然明白为什么要使用数组了,因为方便销毁,如果是vector的话,就很难办
            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; // 这一步实际上是把存放线程ID的内存释放掉了,并不是释放掉了真正的线程。
    
    // 这是在堆中开辟的内存,使用delete将堆中的内存释放掉
    // 释放该内存后,这也相当于释放了线程,那m_stop = true还有必要吗?
    // 或者说pthread_t*指针根本不代表线程,释放m_threads并不会真正释放线程资源
    // pthread_t确实不表示真正的线程,它的真正类型是unsigned long int
    // 表示线程ID,而且它指向的内存与线程资源中的ID所在的内存一定是独立的
    // 否则delete就会把线程资源ID释放掉,这也太滑稽了
    m_stop = true;
}

template<typename T>
bool threadPool<T>::append(T* request){
    m_queueLocker.lock(); // 上锁
    if(m_queue.size() + 1 > m_max_requets){
        m_queueLocker.unlock();
        return false;
    }
    m_queue.push_back(request);
    m_queue.unlock(); // 解锁
    m_queueStat.post(); // 信号量加1
    // 表明有任务到达,唤醒等待队列中阻塞的线程
    return true;
}

template<typename T>
void* threadPool<T>::work(void* args){
    threadPool* pool = static_cast<threadPool*>(args);
    pool -> run();
    return pool;
}

template<typename T>
void threadPool<T>::run(){

    while(!m_stop){
        // 判断是否有任务到达,如果没有任务到达,wait()会阻塞该线程
        m_queueStat.wait();
        m_queueLocker.lock();
        if(m_queue.empty()){
            m_queueLocker.unlock();
            continue;
        }
        T* request = m_queue.front();
        m_queue.pop_front();
        m_queueLocker.unlock();
        if(!request){
            continue;
        }

        request -> process(); // 处理任务
    }
}

 // 我好像明白什么是面向对象了
    // 对象会提供操作自身的成员函数
    // 如果你要处理对象里面的数据,就让对象调用自己的成员函数

    // 而对于面向过程的编程,如果你要处理一些数据,需要把这些数据传递给一个函数
    // 就比如线程池里,如果使用面向过程的编程,要处理任务,需要把待处理的任务通过指针传递进去

错误情况

结果在编译时报了如下错误:

/tmp/cczG6pBx.o:在函数‘main’中:
main.cpp:(.text+0xce):对‘threadPool<http_conn>::threadPool(int, int)’未定义的引用
main.cpp:(.text+0x4da):对‘threadPool<http_conn>::append(http_conn*)’未定义的引用
main.cpp:(.text+0x607):对‘threadPool<http_conn>::~threadPool()’未定义的引用
collect2: error: ld returned 1 exit status

错误原因

由于时间关系,错误原因稍后补充

解决方案

一般而言,有如下解决方案:

  1. 将模板类的声明和定义放在同一个.h文件里
  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值