[项目][WebServer][ThreadPool]详细讲解

  • 单例模式的线程安全

    • 需要双重判空指针,降低锁冲突的概率,提高性能
    • 原因1:
      • 当第一次实例化单例时,可能有多个线程同时到来,并且svr指针为空
      • 这时他们就会去竞争锁,但只有一个线程会最快拿到锁,并且成功实例化出单例对象
      • 但此时如果不加双重判空指针,那些也进了第一层if判断的,仍然会去实例化出对象
    • 原因2:
      • 为了线程安全,必然要加锁,加锁之后再去判空
      • 但每次调用GetInstance()都需要去获得锁,释放锁,效率低下
      • 此时再加一层外层if判空,这样就会避免后续调用GetInstance()时没必要的锁竞争
  • static void *ThreadRoutine(void *args)为什么要设置为static方法?

    • pthread_create传递给线程的方法只能是返回值为void*,参数为void*的函数
    • static将函数方法声明为静态方法,此时该方法没有隐含的this指针,就可以在类内把这个方法传递给线程调用了
      int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                                void *(*start_routine) (void *), void *arg);
      
  • while()防止伪唤醒

    • 可能条件变量唤醒线程时,有多个线程同时被唤醒,但是只有一个最快的线程PopTask()可以拿到任务,此时其他线程就会出错
    • while()可以在被唤醒的情况下,再次判断任务队列是否有任务
    • 这样可以保证,在某个线程醒来的时候,一定是占有互斥锁的
static const int THREAD_POOL_NUM = 10;

// 单例模式
class ThreadPool
{
public:
    static ThreadPool *GetInstance(int num = THREAD_POOL_NUM)
    {
        static pthread_mutex_t sMtx = PTHREAD_MUTEX_INITIALIZER;
        if (_tp == nullptr)
        {
            pthread_mutex_lock(&sMtx);
            if (_tp == nullptr) // 双重判断,以防线程安全问题
            {
                _tp = new ThreadPool(num);
                _tp->Init();
            }
            pthread_mutex_unlock(&sMtx);
        }

        return _tp;
    }

    // static使该成员函数没有this指针,因为线程执行的函数只能有一个void*参数
    static void *ThreadRoutine(void *args)
    {
        ThreadPool *tp = (ThreadPool *)args;

        while(true)
        {
            Task task;
            tp->Lock();
            while(tp->TaskQueueIsEmpty()) // while防止伪唤醒
            {
                tp->ThreadWait();
            }
            tp->Pop(task);
            tp->Unlock(); // 注意,不要在临界资源区内处理任务哦~
            task.ProcessOn();
        }
    }

    bool Init()
    {
        for (int i = 0; i < _num; i++)
        {
            pthread_t tid;
            if (pthread_create(&tid, nullptr, ThreadRoutine, this) != 0)
            {
                LOG(FATAL, "Create ThreadPool Error");
                return false;
            }
        }
        LOG(INFO, "Create ThreadPool Success");
        
        return true;
    }

    void Push(const Task& task) // in
    {
        Lock();
        _taskQueue.push(task); // 任务队列为临界资源,操作要加锁
        Unlock();

        ThreadWakeUp();
    }

    void Pop(Task& task) // out
    {
        task = _taskQueue.front();
        _taskQueue.pop();
    }

    void ThreadWait()
    {
        pthread_cond_wait(&_cond, &_mtx);
    }

    void ThreadWakeUp()
    {
        pthread_cond_signal(&_cond);
    }

    bool TaskQueueIsEmpty()
    {
        return !_taskQueue.size();
    }

    void Lock()
    {
        pthread_mutex_lock(&_mtx);
    }

    void Unlock()
    {
        pthread_mutex_unlock(&_mtx);
    }

    bool IsStop()
    {
        return _stop;
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_cond);
    }
private:
    ThreadPool(int num = THREAD_POOL_NUM)
        : _num(num), _stop(false)
    {
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    ThreadPool(const ThreadPool &) = delete;
private:
    int _num;
    bool _stop;
    std::queue<Task> _taskQueue;
    pthread_mutex_t _mtx;
    pthread_cond_t _cond;
    static ThreadPool *_tp;
};

ThreadPool* ThreadPool::_tp = nullptr;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DieSnowK

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值