【线程池C++实现】

操作系统:Ubuntu 16.04

运行方式:

        g++ -std=c++11 thread_pool_example.cpp -o thread_pool_example -lpthread

        ./thread_pool_example

        最近时间较紧,不能详细展开叙述。具体原理及代码逻辑可以参见注释,十分详细。日后一定补充完善。

#include <iostream>
#include <list>
#include <cstdio>
#include <exception>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <functional>
using namespace std;

// 任务类
class Task
{
public:
    Task(int a, int b)
    {
        this->a = a;
        this->b = b;
    }

    void runTask()
    {
        cout << "task " << a << " + " << b << " = " << a + b << endl;
    }

private:
    int a, b;
};

// 线程池类
class ThreadPool
{
public:
    // 构造函数
    ThreadPool(int threadNum, int maxTask)
    {
        this->threadNum = threadNum;                   // threadNum 是线程池中线程的数量
        this->maxTask = maxTask;                       // maxTask 是求队列中最多允许的、等待处理的请求的数量
        threadsArr = nullptr;                          // 描述线程池的数组,其大小为 threadNum
        stop = false;                                  // 停止标志符
        if (pthread_mutex_init(&locker, nullptr) != 0) // 初始化互斥锁
            throw exception();
        if (sem_init(&sem, 0, 0) != 0) // 初始化信号量
            throw exception();

        if (threadNum <= 0 || maxTask <= 0)
            throw exception();

        threadsArr = new pthread_t[threadNum]; // 线程池申请空间 and 初始化
        if (!threadsArr)
            throw exception();

        /*
            pthread_create 函数参数含义:线程标识符(后续可引用)、设置线程属性(NULL表示为默认属性)、线程将运行的函数及其参数(this)
            注意,pthread_create 函数使用时,第三个参数必须指向一个静态函数,故这里想在静态函数中使用类的动态成员(成员函数 or 变量),
            可以将类的对象(this)作为参数传递给该静态函数(work),然后在静态函数中引用这个对象,并调用其动态方法
            具体的,类对象传递时用this指针,传递给静态函数后,将其转换为线程池类,并调用私有成员函数run。
        */
        for (int i = 0; i < threadNum; ++i)
        {
            if (pthread_create(threadsArr + i, nullptr, worker, this) != 0)
            {
                delete[] threadsArr;
                throw exception();
            }

            /*
                pthread_join:主线程(创建线程者)应该调用 pthread_join 来等待子线程运行结束,并可得到子线程的退出代码,回收其资源(类似于wait,waitpid)
                但是调用 pthread_join(pthread_id) 后,如果该子线程没有运行结束,主(调用者)会被阻塞。但是在 web 服务器中,不希望主线程被阻塞,因为还要
                处理后续的连接,故采用 pthread_detached函数
                pthread_detach:将该子线程的状态设置为 detached,则该线程运行结束后会自动释放所有资源。将线程进行分离后,不用单独对工作线程进行回收
            */
            if (pthread_detach(threadsArr[i]))
            {
                delete[] threadsArr;
                throw exception();
            }
        }
    }

    // 析构函数
    ~ThreadPool()
    {
        delete[] threadsArr;
        sem_destroy(&sem);
        pthread_mutex_destroy(&locker);
        stop = true;
    }

    // 往请求队列中添加任务
    bool append(Task *task)
    {
        // 操作工作队列时,一定要加锁,因为它被所有线程共享
        pthread_mutex_lock(&locker);
        if (taskList.size() > maxTask)
        {
            pthread_mutex_unlock(&locker);
            return false;
        }

        taskList.push_back(task);
        pthread_mutex_unlock(&locker);
        sem_post(&sem); // v 操作,释放一个信号量,以此提醒run()有任务要处理
        return true;
    }

private:
    // 工作线程运行的函数,它不断从工作队列中取出任务并执行之,内部访问私有成员函数run,完成线程处理要求。
    static void *worker(void *arg)
    {
        ThreadPool *pool = (ThreadPool *)arg; // 将参数强转为线程池类,调用成员方法
        pool->run();                          // 调用类的动态函数 run()
        return pool;
    }

    // 取出工作队列头部任务执行
    void run()
    {
        while (!stop)
        {
            sem_wait(&sem);
            pthread_mutex_lock(&locker);
            if (taskList.empty())
            {
                pthread_mutex_unlock(&locker);
                continue;
            }

            Task *task = taskList.front();
            taskList.pop_front();
            pthread_mutex_unlock(&locker);
            if (!task)
                continue;
            task->runTask();
        }
    }

private:
    int threadNum;
    int maxTask;
    pthread_t *threadsArr;
    pthread_mutex_t locker;
    sem_t sem;
    list<Task *> taskList;
    bool stop;
};

int main()
{
    cout << "=== Tasks Begin ===" << endl;

    // 构造线程池
    int threadNum = 2, maxTask = 10;
    ThreadPool *pool = new ThreadPool(threadNum, maxTask);

    // 这里把工作队列单独在主函数中构造出来,是为了后续的 delete,防止空指针的出现
    list<Task *> tasks(10);
    for (int i = 0; i < 10; ++i)
    {
        Task *tmpTask = new Task(i, i);
        tasks.push_back(tmpTask);
        pool->append(tmpTask);
    }

    sleep(5);

    // 防止空指针出现
    for (int i = 0; i < 5; ++i)
    {
        delete tasks.front();
        tasks.front() = nullptr;
        tasks.pop_front();
    }

    cout << "=== Tasks End ===" << endl;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

棱角码农

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

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

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

打赏作者

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

抵扣说明:

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

余额充值