手写线程池 - C++版 - 笔记总结

1.线程池原理

创建一个线程,实现很方便。
缺点:若并发的线程数量很多,并且每个线程都是执行一个时间较短的任务就结束了。
      由于频繁的创建线程和销毁线程需要时间,这样的频繁创建线程会大大降低
      系统的效率。

2.思考 :怎么样实现线程复用,让线程执行完一个任务后不被销毁,还可以继续执行其他的任务?

答:线程池

3.思考 :什么是线程池?

可以点击看爱编程的大丙写的这边文章!写的很好

 4.线程池的组成:任务队列、工作的线程、管理者线程

(1)任务队列
听到任务队列之后,咱们在脑海里应该闪现出另外一个模型,就是生产者和消费者模型。因为只要
有任务队列,就离不开生产者和消费者模型。为什么要有任务队列呢?就是因为他有生产者要把生
产出来的数据或者商品进行存储,存储起来之后让对应的某些消费者去消费。在我们写程序的时候,
就是有一些生产者线程还是负责往这个任务队列里边放任务,然后有一些消费者线程负责把任务从
任务队列中取出去,并且把它处理掉。在线程池里边有任务队列,也就意味着这个线程池它是生产者
和消费者模型里边的一部分。哪一个部分呢?这个线程池主要是负责维护一个任务队列,然后再维护
若干个消费者线程。它维护的线程都是消费者线程。
那么生产者是谁呢?谁使用任务队列,谁就是生产者。那么这个生产者它把任务放到任务队列里边。
怎么放呢?肯定是通过线程池提供的api接口,把这个任务放到任务队列,,放进来之后,这个消费
者线程通过一个循环,不停地从这个任务队列里边去取任务,
假设说咱们这个任务队列为空了,消费者线程就需要阻塞了。可以使用条件变量把它阻塞掉就行了。
如果让消费者线程去阻塞,它就放弃了CPU时间片了,这个对系统的资源消耗是一点都没有的。
对于生产者来说,假设这个任务队列已经满了,需要阻塞生产者。当这个消费者消费了这个产品之后,
任务队列就不再满了,那么任务队列不再满之后,咱们就让消费者把这个生产者唤醒,让他们继续生
产。这个任务队列是两个角色,而不是三个角色,这一点要搞明白的!!!
 
 
 
(2)工作的线程
这个任务队列里边的任务都是函数地址,工作的线程在处理这个任务的时候,它就基于这个函数地址
对那个函数进行调用。也就说这个任务队列里边的任务都是回调函数。
什么时候去回调呢?
就是当线程池里边的这个消费者线程把它取出去之后,它就被调用了。如果说没有被取出来,他就不
被调用了。
 
 
(3)管理者线程
管理者线程的职责非常单一,就是不停的去检测当前任务队列里边任务的个数,还有当前工作的线程
的线程数,然后针对于它们的数量进行一个运算。看一看现在需要添加线程还是销毁线程。干这个事
的时候,可以给它设置一个频率,比如说你可以让他五秒钟去做一次或者十秒钟去做一次。sleep管理
者是非常轻松的

(一)任务类Task的定义

#pragma once
#include <queue>
#include <pthread.h>

using callback = void (*)(void *);

// 任务结构体
template <typename T>
struct Task
{
    Task<T>()
    {
        function = nullptr;
        arg = nullptr;
    }
    Task<T>(callback f, void *arg)
    {
        this->arg = (T *)arg;
        function = f;
    }
    callback function;
    T *arg;
};

(二)任务队列TaskQueue的定义

template <typename T>
class TaskQueue
{
public:
    TaskQueue();
    ~TaskQueue();

    // 添加任务
    void addTask(Task<T> task);
    void addTask(callback f, void *arg);

    // 取出一个任务
    Task<T> takeTask();
    // 获取当前任务的个数
    inline size_t taskNumber()
    {
        return m_taskQ.size();
    }

private:
    std::queue<Task<T> > m_taskQ;
    pthread_mutex_t m_mutex;
};

(三)线程池 ThreadPool 类的声明

#pragma once
#include "TaskQueue.h"
template <typename T>
class ThreadPool
{
public:
    // 创建线程池并初始化
    ThreadPool(int min, int max);

    // 销毁线程池
    ~ThreadPool();

    // 给线程池添加任务
    void addTask(Task<T> task);

    // 获取线程池中工作的线程的个数
    int getBusyNum();

    // 获取线程池中活着的线程个数
    int getAliveNum();

private:
    // 工作的线程是(消费者线程)任务函数
    static void *worker(void *arg);
    // 管理者线程任务函数
    static void *manager(void *arg);
    // 单个线程退出
    void threadExit();

private:
    // 任务队列
    TaskQueue<T> *taskQ;

    pthread_t managerID;       // 管理者线程ID
    pthread_t *threadIDs;      // 工作的线程ID
    int minNum;                // 最小线程数量
    int maxNum;                // 最大线程数量
    int busyNum;               // 忙的线程的个数
    int liveNum;               // 存活的线程的个数
    int exitNum;               // 要销毁的线程个数
    pthread_mutex_t mutexPool; // 锁整个的线程池
    pthread_cond_t notEmpty;   // 任务队列是不是空了

    bool shutdown = false; // 是不是要销毁线程池
    static const int NUMBER = 2;
};

(四)线程池 构造函数

template <typename T>
ThreadPool<T>::ThreadPool(int min, int max)
{
    do
    {
        // 实例化任务队列
        taskQ = new TaskQueue<T>;
        if (taskQ == nullptr)
        {
            std::cout << "new taskQ fail..." << std::endl;
            break;
        }

        // 根据线程的最大上限给线程数组分配内存
        threadIDs = new pthread_t[max];
        if (threadIDs == nullptr)
        {
            std::cout << "new threadIDs fail..." << std::endl;
            break;
        }
        // 初始化
        memset(threadIDs, 0, sizeof(pthread_t) * max);
        minNum = min;
        maxNum = max;
        busyNum = 0;
        liveNum = min; // 和最小个数相等
        exitNum = 0;

        // 初始化互斥锁,条件变量
        if (pthread_mutex_init(&mutexPool, NULL) != 0 ||
            pthread_cond_init(&notEmpty, NULL) != 0)
        {
            std::cout << "mutex or condition init fail...\n"
                      << std::endl;
            break;
        }

        shutdown = false;
        /// 创建线程 //
        //"void *(ThreadPool::*)(void *arg)" 类型的实参与 "void *(*)(void *)" 类型的形参不兼容
        // 类的静态成员或者类的外部的非类普通函数,只要定义出来之后就有地址
        // 如果是类的成员函数,并且不是静止的,这个函数定义出来是没有地址的
        // 什么时候有地址呢?我们必须要给这个类进行实例化,创建类的对象,这个
        // 函数才有地址
        // this指针指向当前被实例化的对象,如果要是在外边new出来一个ThreadPool对象,
        // 那么这个this指针就指向该实例化对象
        // 为什么要把this传给manager呢?
        // 因为manager是一个静态方法,静态方法它只能访问类里边的静态成员变量,
        // 它不能访问类的非静态成员变量。
        // 因此如果我们想要访问这些非静态成员变量,就必须要给这个静态方法传进去一个
        // 实例化对象,通过传进去的这个实例化对象来访问里边的非静态成员函数和变量

        // 创建管理者线程,1个
        pthread_create(&managerID, NULL, manager, this);
        // 根据最小线程个数,创建线程
        for (int i = 0; i < min; i++)
        {
            pthread_create(&threadIDs[i], NULL, worker, this);
        }
        return;
    } while (0);
    // 释放资源
    if (threadIDs)
        delete[] threadIDs;
    if (taskQ)
        delete taskQ;
}

(五)工作的线程的任务函数

// 工作线程任务函数
template <typename T>
void *ThreadPool<T>::worker(void *arg)
{
    ThreadPool *pool = static_cast<ThreadPool *>(arg);
    // 一直不停的工作
    while (true)
    {
        // 访问任务队列(共享资源)加锁
        pthread_mutex_lock(&pool->mutexPool);
        // 当前任务队列是否为空,如果为空工作线程阻塞
        while (pool->taskQ->taskNumber() == 0 && !pool->shutdown)
        {
            //printf("thread %ld waiting...\n",pthread_self());
            // 阻塞工作线程
            pthread_cond_wait(&pool->notEmpty, &pool->mutexPool);
            // 解除阻塞之后,判断是不是要销毁线程
            if (pool->exitNum > 0)
            {
                pool->exitNum--;
                if (pool->liveNum > pool->minNum)
                {
                    pool->liveNum--;
                    pthread_mutex_unlock(&pool->mutexPool);
                    pool->threadExit();
                }
            }
        }
        // 判断线程池是否被关闭了
        if (pool->shutdown)
        {
            pthread_mutex_unlock(&pool->mutexPool);
            pool->threadExit();
        }
        // 从任务队列中取出一个任务
        Task<T> task = pool->taskQ->takeTask();
        // 工作的线程+1
        pool->busyNum++;
        // 线程池解锁
        pthread_mutex_unlock(&pool->mutexPool);

        // 执行任务
        printf("thread %ld start working...\n", pthread_self());
        // std::cout<<"thread "<<to_string(pthread_self()) <<" start working..."<<std::endl;

        task.function(task.arg);
        delete task.arg;
        task.arg = nullptr;

        // 任务处理结束
        printf("thread %ld end working...\n", pthread_self());
        // std::cout<<"thread "<<std::string to_string(pthread_self()) <<" end working..."<<std::endl;

        pthread_mutex_lock(&pool->mutexPool);
        pool->busyNum--;
        pthread_mutex_unlock(&pool->mutexPool);
    }
    return nullptr;
}

(六)线程退出函数

//线程退出
template <typename T>
void ThreadPool<T>::threadExit()
{
    pthread_t tid = pthread_self();
    for (int i = 0; i < maxNum; ++i)
    {
        if (threadIDs[i] == tid)
        {
            threadIDs[i] = 0;
            // std::cout<<"threadExit() called, "<<std::string to_string(tid)<<" exiting..."<<std::endl;
            printf("threadExit() called,%ld exiting...\n", tid);
            break;
        }
    }
    pthread_exit(NULL); // 这个是标准C的函数
}

(七)管理者线程的任务函数

// 管理者线程任务函数
template <typename T>
void *ThreadPool<T>::manager(void *arg)
{
    ThreadPool *pool = static_cast<ThreadPool *>(arg);
    // 如果线程池没有关闭,就一直检测
    while (!pool->shutdown)
    {
        // 每隔3s检测一次
        sleep(3);

        // 取出线程池中任务的数量和当前线程的数量 取出(工作的)忙的线程的数量
        pthread_mutex_lock(&pool->mutexPool);
        int queueSize = pool->taskQ->taskNumber();
        int liveNum = pool->liveNum;
        int busyNum = pool->busyNum;
        pthread_mutex_unlock(&pool->mutexPool);

        // 添加线程
        // 任务的个数>存货的线程个数 && 存活的线程数 < 最大线程数
        if (queueSize > liveNum && liveNum < pool->maxNum)
        {
            pthread_mutex_lock(&pool->mutexPool);
            int counter = 0;
            for (int i = 0; i < pool->maxNum && counter < NUMBER && pool->liveNum < pool->maxNum; ++i)
            {
                if (pool->threadIDs[i] == 0)
                {
                    pthread_create(&pool->threadIDs[i], NULL, worker, pool);
                    counter++;
                    pool->liveNum++;
                }
            }
            pthread_mutex_unlock(&pool->mutexPool);
        }

        // 销毁多余的线程
        // 忙的线程 * 2 < 存活的线程数 && 存活的线程 > 最小线程数
        if (busyNum * 2 < liveNum && liveNum > pool->minNum)
        {
            pthread_mutex_lock(&pool->mutexPool);
            pool->exitNum = NUMBER;
            pthread_mutex_unlock(&pool->mutexPool);
            // 让工作的线程自杀
            for (int i = 0; i < NUMBER; ++i)
            {
                pthread_cond_signal(&pool->notEmpty);
            }
        }
    }
    return nullptr;
}

(八)给线程池添加任务

// 给线程池添加任务
template <typename T>
void ThreadPool<T>::addTask(Task<T> task)
{
    if (shutdown)
    {
        return;
    }
    // 添加任务,不需要加锁,任务队列中有锁
    taskQ->addTask(task);
    // 唤醒工作的线程
    pthread_cond_signal(&notEmpty); // 通知消费者消费
}

(九)获取线程池忙的线程个数和活着的线程个数

template <typename T>
int ThreadPool<T>::getBusyNum()
{
    pthread_mutex_lock(&mutexPool);
    int busyNum = this->busyNum;
    pthread_mutex_unlock(&mutexPool);
    return busyNum;
}

template <typename T>
int ThreadPool<T>::getAliveNum()
{
    pthread_mutex_lock(&mutexPool);
    int aliveNum = this->liveNum;
    pthread_mutex_unlock(&mutexPool);
    return aliveNum;
}

(十)线程池析构

template <typename T>
ThreadPool<T>::~ThreadPool()
{
    // 关闭线程池
    shutdown = true;
    // 阻塞回收管理者线程
    pthread_join(managerID, NULL);
    // 唤醒阻塞的消费者线程
    for (int i = 0; i < liveNum; i++)
    {
        pthread_cond_signal(&notEmpty);
    }
    // 释放堆内存
    if (taskQ) delete taskQ;
    if (threadIDs) delete[] threadIDs;
    pthread_mutex_destroy(&mutexPool);
    pthread_cond_destroy(&notEmpty);
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

完整代码:

TaskQueue.h

#pragma once
#include <queue>
#include <pthread.h>

using callback = void (*)(void *);

// 任务结构体
template <typename T>
struct Task
{
    Task<T>()
    {
        function = nullptr;
        arg = nullptr;
    }
    Task<T>(callback f, void *arg)
    {
        this->arg = (T *)arg;
        function = f;
    }
    callback function;
    T *arg;
};

template <typename T>
class TaskQueue
{
public:
    TaskQueue();
    ~TaskQueue();

    // 添加任务
    void addTask(Task<T> task);
    void addTask(callback f, void *arg);

    // 取出一个任务
    Task<T> takeTask();
    // 获取当前任务的个数
    inline size_t taskNumber()
    {
        return m_taskQ.size();
    }

private:
    std::queue<Task<T> > m_taskQ;
    pthread_mutex_t m_mutex;
};

TaskQueue.cpp

#include "TaskQueue.h"
#include <pthread.h>

template <typename T>
TaskQueue<T>::TaskQueue()
{
    // 初始化互斥锁
    pthread_mutex_init(&m_mutex, NULL);
}

template <typename T>
TaskQueue<T>::~TaskQueue()
{
    // 销毁互斥锁
    pthread_mutex_destroy(&m_mutex);
}

template <typename T>
void TaskQueue<T>::addTask(Task<T> task)
{
    pthread_mutex_lock(&m_mutex);
    m_taskQ.push(task);
    pthread_mutex_unlock(&m_mutex);
}

template <typename T>
Task<T> TaskQueue<T>::takeTask()
{
    Task<T> t;
    pthread_mutex_lock(&m_mutex);
    if (!m_taskQ.empty())
    {
        t = m_taskQ.front();
        m_taskQ.pop();
    }
    pthread_mutex_unlock(&m_mutex);
    return t;
}

template <typename T>
void TaskQueue<T>::addTask(callback f, void *arg)
{
    pthread_mutex_lock(&m_mutex);
    m_taskQ.push(Task<T>(f, arg));
    pthread_mutex_unlock(&m_mutex);
}

ThreadPool.h

#pragma once
#include "TaskQueue.h"
template <typename T>
class ThreadPool
{
public:
    // 创建线程池并初始化
    ThreadPool(int min, int max);

    // 销毁线程池
    ~ThreadPool();

    // 给线程池添加任务
    void addTask(Task<T> task);

    // 获取线程池中工作的线程的个数
    int getBusyNum();

    // 获取线程池中活着的线程个数
    int getAliveNum();

private:
    // 工作的线程是(消费者线程)任务函数
    static void *worker(void *arg);
    // 管理者线程任务函数
    static void *manager(void *arg);
    // 单个线程退出
    void threadExit();

private:
    // 任务队列
    TaskQueue<T> *taskQ;

    pthread_t managerID;       // 管理者线程ID
    pthread_t *threadIDs;      // 工作的线程ID
    int minNum;                // 最小线程数量
    int maxNum;                // 最大线程数量
    int busyNum;               // 忙的线程的个数
    int liveNum;               // 存活的线程的个数
    int exitNum;               // 要销毁的线程个数
    pthread_mutex_t mutexPool; // 锁整个的线程池
    pthread_cond_t notEmpty;   // 任务队列是不是空了

    bool shutdown = false; // 是不是要销毁线程池
    static const int NUMBER = 2;
};

ThreadPool.cpp

#include "ThreadPool.h"
#include <iostream>
#include <string.h>
#include <stdlib.h>

#include <unistd.h>

template <typename T>
ThreadPool<T>::ThreadPool(int min, int max)
{
    do
    {
        // 实例化任务队列
        taskQ = new TaskQueue<T>;
        if (taskQ == nullptr)
        {
            std::cout << "new taskQ fail..." << std::endl;
            break;
        }

        // 根据线程的最大上限给线程数组分配内存
        threadIDs = new pthread_t[max];
        if (threadIDs == nullptr)
        {
            std::cout << "new threadIDs fail..." << std::endl;
            break;
        }
        // 初始化
        memset(threadIDs, 0, sizeof(pthread_t) * max);
        minNum = min;
        maxNum = max;
        busyNum = 0;
        liveNum = min; // 和最小个数相等
        exitNum = 0;

        // 初始化互斥锁,条件变量
        if (pthread_mutex_init(&mutexPool, NULL) != 0 ||
            pthread_cond_init(&notEmpty, NULL) != 0)
        {
            std::cout << "mutex or condition init fail...\n"
                      << std::endl;
            break;
        }

        shutdown = false;
        /// 创建线程 //
        //"void *(ThreadPool::*)(void *arg)" 类型的实参与 "void *(*)(void *)" 类型的形参不兼容
        // 类的静态成员或者类的外部的非类普通函数,只要定义出来之后就有地址
        // 如果是类的成员函数,并且不是静止的,这个函数定义出来是没有地址的
        // 什么时候有地址呢?我们必须要给这个类进行实例化,创建类的对象,这个
        // 函数才有地址
        // this指针指向当前被实例化的对象,如果要是在外边new出来一个ThreadPool对象,
        // 那么这个this指针就指向该实例化对象
        // 为什么要把this传给manager呢?
        // 因为manager是一个静态方法,静态方法它只能访问类里边的静态成员变量,
        // 它不能访问类的非静态成员变量。
        // 因此如果我们想要访问这些非静态成员变量,就必须要给这个静态方法传进去一个
        // 实例化对象,通过传进去的这个实例化对象来访问里边的非静态成员函数和变量

        // 创建管理者线程,1个
        pthread_create(&managerID, NULL, manager, this);
        // 根据最小线程个数,创建线程
        for (int i = 0; i < min; i++)
        {
            pthread_create(&threadIDs[i], NULL, worker, this);
        }
        return;
    } while (0);
    // 释放资源
    if (threadIDs)
        delete[] threadIDs;
    if (taskQ)
        delete taskQ;
}

// 工作线程任务函数
template <typename T>
void *ThreadPool<T>::worker(void *arg)
{
    ThreadPool *pool = static_cast<ThreadPool *>(arg);
    // 一直不停的工作
    while (true)
    {
        // 访问任务队列(共享资源)加锁
        pthread_mutex_lock(&pool->mutexPool);
        // 当前任务队列是否为空,如果为空工作线程阻塞
        while (pool->taskQ->taskNumber() == 0 && !pool->shutdown)
        {
            //printf("thread %ld waiting...\n",pthread_self());
            // 阻塞工作线程
            pthread_cond_wait(&pool->notEmpty, &pool->mutexPool);
            // 解除阻塞之后,判断是不是要销毁线程
            if (pool->exitNum > 0)
            {
                pool->exitNum--;
                if (pool->liveNum > pool->minNum)
                {
                    pool->liveNum--;
                    pthread_mutex_unlock(&pool->mutexPool);
                    pool->threadExit();
                }
            }
        }
        // 判断线程池是否被关闭了
        if (pool->shutdown)
        {
            pthread_mutex_unlock(&pool->mutexPool);
            pool->threadExit();
        }
        // 从任务队列中取出一个任务
        Task<T> task = pool->taskQ->takeTask();
        // 工作的线程+1
        pool->busyNum++;
        // 线程池解锁
        pthread_mutex_unlock(&pool->mutexPool);

        // 执行任务
        printf("thread %ld start working...\n", pthread_self());
        // std::cout<<"thread "<<to_string(pthread_self()) <<" start working..."<<std::endl;

        task.function(task.arg);
        delete task.arg;
        task.arg = nullptr;

        // 任务处理结束
        printf("thread %ld end working...\n", pthread_self());
        // std::cout<<"thread "<<std::string to_string(pthread_self()) <<" end working..."<<std::endl;

        pthread_mutex_lock(&pool->mutexPool);
        pool->busyNum--;
        pthread_mutex_unlock(&pool->mutexPool);
    }
    return nullptr;
}

//线程退出
template <typename T>
void ThreadPool<T>::threadExit()
{
    pthread_t tid = pthread_self();
    for (int i = 0; i < maxNum; ++i)
    {
        if (threadIDs[i] == tid)
        {
            threadIDs[i] = 0;
            // std::cout<<"threadExit() called, "<<std::string to_string(tid)<<" exiting..."<<std::endl;
            printf("threadExit() called,%ld exiting...\n", tid);
            break;
        }
    }
    pthread_exit(NULL); // 这个是标准C的函数
}

// 管理者线程任务函数
template <typename T>
void *ThreadPool<T>::manager(void *arg)
{
    ThreadPool *pool = static_cast<ThreadPool *>(arg);
    // 如果线程池没有关闭,就一直检测
    while (!pool->shutdown)
    {
        // 每隔3s检测一次
        sleep(3);

        // 取出线程池中任务的数量和当前线程的数量 取出(工作的)忙的线程的数量
        pthread_mutex_lock(&pool->mutexPool);
        int queueSize = pool->taskQ->taskNumber();
        int liveNum = pool->liveNum;
        int busyNum = pool->busyNum;
        pthread_mutex_unlock(&pool->mutexPool);

        // 添加线程
        // 任务的个数>存货的线程个数 && 存活的线程数 < 最大线程数
        if (queueSize > liveNum && liveNum < pool->maxNum)
        {
            pthread_mutex_lock(&pool->mutexPool);
            int counter = 0;
            for (int i = 0; i < pool->maxNum && counter < NUMBER && pool->liveNum < pool->maxNum; ++i)
            {
                if (pool->threadIDs[i] == 0)
                {
                    pthread_create(&pool->threadIDs[i], NULL, worker, pool);
                    counter++;
                    pool->liveNum++;
                }
            }
            pthread_mutex_unlock(&pool->mutexPool);
        }

        // 销毁多余的线程
        // 忙的线程 * 2 < 存活的线程数 && 存活的线程 > 最小线程数
        if (busyNum * 2 < liveNum && liveNum > pool->minNum)
        {
            pthread_mutex_lock(&pool->mutexPool);
            pool->exitNum = NUMBER;
            pthread_mutex_unlock(&pool->mutexPool);
            // 让工作的线程自杀
            for (int i = 0; i < NUMBER; ++i)
            {
                pthread_cond_signal(&pool->notEmpty);
            }
        }
    }
    return nullptr;
}

// 给线程池添加任务
template <typename T>
void ThreadPool<T>::addTask(Task<T> task)
{
    if (shutdown)
    {
        return;
    }
    // 添加任务,不需要加锁,任务队列中有锁
    taskQ->addTask(task);
    // 唤醒工作的线程
    pthread_cond_signal(&notEmpty); // 通知消费者消费
}

template <typename T>
int ThreadPool<T>::getBusyNum()
{
    pthread_mutex_lock(&mutexPool);
    int busyNum = this->busyNum;
    pthread_mutex_unlock(&mutexPool);
    return busyNum;
}

template <typename T>
int ThreadPool<T>::getAliveNum()
{
    pthread_mutex_lock(&mutexPool);
    int aliveNum = this->liveNum;
    pthread_mutex_unlock(&mutexPool);
    return aliveNum;
}

template <typename T>
ThreadPool<T>::~ThreadPool()
{
    // 关闭线程池
    shutdown = true;
    // 阻塞回收管理者线程
    pthread_join(managerID, NULL);
    // 唤醒阻塞的消费者线程
    for (int i = 0; i < liveNum; i++)
    {
        pthread_cond_signal(&notEmpty);
    }
    // 释放堆内存
    if (taskQ) delete taskQ;
    if (threadIDs) delete[] threadIDs;
    pthread_mutex_destroy(&mutexPool);
    pthread_cond_destroy(&notEmpty);
}

main.cpp

#include <iostream>
#include <pthread.h>
#include "ThreadPool.h"
#include "ThreadPool.cpp"
#include "TaskQueue.cpp"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

void taskFunc(void *arg)
{
    int num = *(int *)arg;
    printf("thread %ld is working, number = %d \n", pthread_self(), num);
    sleep(1);
}

int main()
{
    // 创建线程池
    ThreadPool<int> pool(3, 10);
    for (int i = 0; i < 100; ++i)
    {
        int *num = new int(i + 100);
        *num = i + 100;
        pool.addTask(Task<int>(taskFunc, num));
    }
    sleep(20);
    return 0;
}

跟着这个老师的教程学习的,总结的笔记!!! 

手写线程池 - C改C++版 | 爱编程的大丙 (subingwen.cn)icon-default.png?t=N6B9https://subingwen.cn/linux/threadpool-cpp/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/50967.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Maven基础之项目创建、packaging

文章目录 创建 maven 项目流程骨架是浮云&#xff0c;packaging 是关键 创建 maven 项目流程 通过骨架&#xff08;archetype&#xff09;创建 maven 工程 第一步&#xff1a;选择 new → maven → Maven Project 第二步&#xff1a;New Maven Project 窗口不作任何设置&…

Zabbix监控ActiveMQ

当我们在线上使用了ActiveMQ 后&#xff0c;我们需要对一些参数进行监控&#xff0c;比如 消息是否有阻塞&#xff0c;哪个消息队列阻塞了&#xff0c;总的消息数是多少等等。下面我们就通过 Zabbix 结合 Python 脚本来实现对 ActiveMQ的监控。 一、创建 Activemq Python 监控…

Java 异常处理的使用和思考

概念 异常处理的概念起源于早期的编程语言&#xff0c;如 LISP、PL/I 和 CLU。这些编程语言首次引入了异常处理机制&#xff0c;以便在程序执行过程中检测和处理错误情况。异常处理机制随后在 Ada、Modula-3、C、Python、Java 等编程语言中得到了广泛采用和发展。在 Java 中&a…

Unity Addressable

Unity重要目录 工程中的几个重要目录 Assets存放资源、代码、配置Library大部分的资源导入到Assets目录之后&#xff0c;会转化成Unity认可的文件&#xff0c;转化后的文件会存储在这个目录Logs日志文件Packages第三方插件ProjectSettings存放各种项目设定UserSettings用户偏好…

基于Truss+Docker+Kubernetes把开源模型Falcon-7B送上云端(译)

背景 到目前为止&#xff0c;我们已经看到了ChatGPT的能力及其所能提供的强大功能。然而&#xff0c;对于企业应用来说&#xff0c;像ChatGPT这样的封闭源代码模型可能会带来风险&#xff0c;因为企业自身无法控制他们的数据。尽管OpenAI公司声称用户数据不会被存储或用于训练…

python 将pdf文件转图片

有小伙伴问了怎么将 pdf文件转图片的问题&#xff0c;我百度了一波儿&#xff0c;搞了以下python代码给他封装成exe工具了。 中途打包踩了个坑&#xff0c;python进程池的问题&#xff0c;本地运行没啥问题&#xff0c;打包好的exe文件双击就会使电脑内存爆破卡死&#xff0c;…

缩略所写的代码

有一长串的代码需要进行缩略 可以在要缩略的代码的前一行加上注释。并在其中写上 #region。 在最后一行的下一行加上注释&#xff0c;并在其中写上 #endregion。 最终结果&#xff1a;

神经数据库:用于使用 ChatGPT 构建专用 AI 代理的下一代上下文检索系统 — (第 2/3 部分)

书接上回理解构建LLM驱动的聊天机器人时的向量数据库检索的局限性 - &#xff08;第1/3部分&#xff09;_阿尔法旺旺的博客-CSDN博客 其中我们强调了&#xff08;1&#xff09;嵌入生成&#xff0c;然后&#xff08;2&#xff09;使用近似近邻&#xff08;ANN&#xff09;搜索…

25.9 matlab里面的10中优化方法介绍—— 惩罚函数法求约束最优化问题(matlab程序)

1.简述 一、算法原理 1、问题引入 之前我们了解过的算法大部分都是无约束优化问题&#xff0c;其算法有&#xff1a;黄金分割法&#xff0c;牛顿法&#xff0c;拟牛顿法&#xff0c;共轭梯度法&#xff0c;单纯性法等。但在实际工程问题中&#xff0c;大多数优化问题都属于有约…

AI帮你制作海报

介绍 Microsoft Designer是由微软推出的图像处理软件&#xff0c;能够通过套用模板等方式快速完成设计加工&#xff0c;生成能够在社交媒体使用的图片。Designer的使用更为简单便捷&#xff0c;用户能够通过套用模板等方式快速完成设计加工&#xff0c;生成能够在社交媒体使用…

【计算机网络】应用层协议 -- HTTP协议

文章目录 1. 认识HTTP协议2. 认识URL3. HTTP协议格式3.1 HTTP请求协议格式3.2 HTTP响应协议格式 4. HTTP的方法5. HTTP的状态码6. HTTP的Header7. Cookie和Session 1. 认识HTTP协议 协议。网络协议的简称&#xff0c;网络协议是通信计算机双方必须共同遵守的一组约定&#xff0…

Rust vs Go:常用语法对比(五)

题图来自 Rust vs Go 2023[1] 81. Round floating point number to integer Declare integer y and initialize it with the rounded value of floating point number x . Ties (when the fractional part of x is exactly .5) must be rounded up (to positive infinity). 按规…

oCPC实践录 | oCPC下机制设计变得毫无意义?(2)无声的战争

接上回oCPC实践录 | oCPC下机制设计变得毫无意义&#xff1f;&#xff08;1&#xff09;事出异常必有妖&#xff0c;互联网广告最开始采用的广义第一价格密封拍卖&#xff08;GFP)&#xff0c;对广告主而言&#xff0c;需要不断感知竞争对手的变化&#xff0c;修改报价&#xf…

BIOS相关知识

简介 BIOS&#xff08;Basic Input Output System&#xff09;基本输入输出系统&#xff0c;固化在服务器主板的专用ROM中&#xff0c;是加载在服务器硬件系统上最基本的运行程序。BIOS位于硬件和系统中间&#xff0c;用来初始化硬件&#xff0c;为操作系统运行做准备 功能 …

ES6 - promise.all和race方法的用法详解

文章目录 一、前言二、Promise.all()1&#xff0c;第一句&#xff1a;Promise.all()方法接受一个数组作为参数&#xff0c;且每一个都是 Promise 实例2&#xff0c;第二句&#xff1a;如果不是&#xff0c;就会先调Promise.resolve方法&#xff0c;将参数转为 Promise 实例再进…

shell脚本:数据库的分库分表

#!/bin/bash ######################### #File name:db_fen.sh #Version:v1.0 #Email:admintest.com #Created time:2023-07-29 09:18:52 #Description: ########################## MySQL连接信息 db_user"root" db_password"RedHat123" db_cmd"-u${…

c语言位段知识详解

本篇文章带来位段相关知识详细讲解&#xff01; 如果您觉得文章不错&#xff0c;期待你的一键三连哦&#xff0c;你的鼓励是我创作的动力之源&#xff0c;让我们一起加油&#xff0c;一起奔跑&#xff0c;让我们顶峰相见&#xff01;&#xff01;&#xff01; 目录 一.什么是…

某文化馆三维建模模型-glb格式-三维漫游-室内导航测试

资源描述 某文化馆某个楼层的三维建模模型&#xff0c;glb格式&#xff0c;适用于three.js开发&#xff0c;可用来做一些三维室内漫游测试和室内导航测试 资源下载地址

06. 管理Docker容器数据

目录 1、前言 2、Docker实现数据管理的方式 2.1、数据卷&#xff08;Data Volumes&#xff09; 2.2、数据卷容器&#xff08;Data Volume Containers&#xff09; 3、简单示例 3.1、数据卷示例 3.2、数据卷容器示例 1、前言 在生产环境中使用 Docker&#xff0c;一方面…

了解Unity编辑器之组件篇Scripts(六)

Scripts&#xff1a;有Unity提供的一些脚本插件&#xff08;自己新建的脚本也会出现在里面&#xff09; 一、TMPro&#xff1a;有一些与文字显示和排版相关的脚本 1.TextContainer&#xff08;文本容器&#xff09;&#xff1a;TextContainer 是一个内容框&#xff0c;用于定…