Linux简化版线程池

本文介绍了Linux环境下简化版线程池的设计与实现,包括线程池设计思想、应用场景、线程池准备、成员介绍、单例模式设计、任务执行与管理等。线程池通过预先创建线程并复用,提高了处理频繁短任务的效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一,线程池设计

二,线程池应用场景

三,线程池准备

1,包装一个锁

2,一个任务类

三,线程池

1,成员介绍

2,设计单例模式

3,创建线程池

4,线程池执行的任务

5,线程池取任务和加任务

6,整体代码实现

 四,线程池使用

五,谢谢各位佬的观看


一,线程池设计

        我们这个简单线程池设计思想就是提前创建好一批线程,在没有任务的时候就在条件变量下等待,有任务了就唤醒线程去执行任务,执行完了之后不要释放,在条件变量下等待执行任务就好了。准点就在这里,线程创建好了之后执完任务不释放,这大大提高了效率。

       我们把我们的main函数就当作一个创建任务的线程就好了,创建好了之后就不需要管了,唤醒线程池中等待的线程就好了,交给线程池去处理。线程池就不断的处理任务,处理完后继续等待。

二,线程池应用场景

        就目前理解的话,线程池对于那种处理时间短的任务是非常赚的,频繁的短任务,用线程池处理完之后那个线程马上返回线程池继续处理下一个任务,因此如果是许多很长的任务的话就不太适合使用线程池,直接用普通的线程去处理就好了。

三,线程池准备

1,包装一个锁

    包装一个生命周期随对象的锁,这里起始时练习一下智能指针的思想,可以完全不搞这一个的,但是我写了,就懒得在该回去了。其实也简单,就是在创建对象的时候在构造函数中加锁,对象处理作用域析构的时候我们在析构函数里面解锁。

#pragma once

#include <iostream>
class Mutex
{

public:
    Mutex()
    {
        pthread_mutex_init(&lock_, nullptr);
    }
    ~Mutex()
    {
        pthread_mutex_destroy(&lock_);
    }
    void lock()
    {
        pthread_mutex_lock(&lock_);
    }
    void unlock()
    {
        pthread_mutex_unlock(&lock_);
    }
private:
    pthread_mutex_t lock_;
};
// 让锁的生命周期最对象的生命周期。
class LockGuard
{
    public:
    // 构造对象的时候加锁。
    LockGuard(Mutex* mutex)
        : mutex_(mutex)
    {
        mutex_->lock();
    }
    ~LockGuard()
    {
        mutex_->unlock();
    }
private:
    Mutex *mutex_;
};

2,一个任务类

      不然创建任务就是插入数据,处理任务就是拿数据有点太单调了,我们这里任务类不写了,用上一节写的处理+-*/%的简答网络计算器。

#pragma once

#include <iostream>
#include <string>

// 一个任务类,并且他自己实现了处理任务的方法。
class Task
{
public:
    Task(int one = 1, int two = 1, char op = '*')
        : one_(one), two_(two), op_(op)
    {

    }
    // 析构不需要写。

    // 处理任务
    int run()
    {
        int resault = -1;
        switch (op_)
        {
        case '+':
            resault = one_ + two_;
            break;
        case '-':
            resault = one_ - two_;
            break;
        case '*':
            resault = one_ * two_;
            break;
        case '/':
            if (two_ == 0)
            {
                resault = -1;
                flag = false;
            }
            else
            {
                resault = one_ / two_;
            }
            break;
        case '%':
            if (two_ == 0)
            {
                resault = -1;
                flag = false;
            }
            else
            {
                resault = one_ % two_;
            }
            break;
        default:
            flag = false;
            resault = -1;
            break;
        }
        return resault;
    }

    // 用算符重载。
    int operator() ()
    {
        return run();
    }

    // 把需要处理的任务用输出型参数带回,方便打印。
    void getdata(int *one, int *two, char *op)
    {
        *one = one_;
        *two = two_;
        *op = op_;
    }
    bool istrue()
    {
        return flag;
    }
private:
    int one_;
    int two_;
    char op_;
    bool flag = true;
};

三,线程池

1,成员介绍

      我们需要一个互斥锁来保护临界资源。

      一个条件变量在没有任务的时候让线程等待。

     一个队列来保存任务。

     一个标记位来确保线程不会应为多次启动线程池而创建多过的线程。

     一个整形表示线程池中线程数量。

     一个自己的static 指针,我们把线程池搞成单例模式。

2,设计单例模式

构造函数私有:

禁掉拷贝构造,赋值重载

留下一个接口来创建对象,

       这个接口只会在第一次调用的时候创建一个对象,之后的调用只会返回这个对象的地址。

3,创建线程池

 

4,线程池执行的任务

5,线程池取任务和加任务

6,整体代码实现

#pragma once
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <queue>
#include <cassert>
#include "Lock.hpp"
#include"Task.hpp"
using namespace std;

template <class T>
class ThreadPool
{

public:

    ~ThreadPool()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }

    // 准备搞成单例模式的。
    ThreadPool(const ThreadPool<T> &in) = delete;
    void operator=(const ThreadPool<T> &in) = delete;

    // 单例模式一般通过一个接口获取对象,第一次获取是创建,之后直接返回对象的指针。
    static ThreadPool<T> *getinstance()
    {
        static Mutex mutex;
        if (instance_ == nullptr)
        {
            // 构建对象是自动加锁。
            LockGuard LockGuard(&mutex);

            if (instance_ == nullptr)
            {
                instance_ = new ThreadPool<T>();
            }
        }
        // 处理作用域自动解锁。
        return instance_;
    }


//这里要传自己的参数,成员函数内定义线程函数一定要加 static;
    static void *threadrooting(void* args)
    {
        pthread_detach(pthread_self());

        ThreadPool<T> *dp = (ThreadPool<T>*)args;

        while (true)
        {
             dp->LockQueue();

            while (!dp->HaveTask())
            {
                dp->WaitForTask();
            }

            // 走到s这里,有任务的。
            T tmp = dp->pop();
            dp->unLockQueue();
         int one;
         int two;
         char op;
         tmp.getdata(&one,&two,&op);
         cout<<"线程:"<<pthread_self()<<"执行任务:"<<one<<op<<two<<"="<<tmp()<<endl;
        }
    }

    void start()
    {
        assert(!isstart_);
        for (int i = 0; i < threadnum_; i++)
        {
            pthread_t t1;
            pthread_create(&t1, nullptr, threadrooting,this);
        }
    }

    // 这里不需要加锁了,因为线程处理方法那里加过锁了。注意了不要加不必要的锁影响效率。
    T pop()
    {
        T tmp = Taskqueue_.front();
        Taskqueue_.pop();
        return tmp;
    }

    void push(const T& in)
    {
        LockQueue();
        Taskqueue_.push(in);
        unLockQueue();

        //唤醒一个线程去处理这个任务。
        AwakenForThread();
    }

private:
    ThreadPool(int threadnum = 5)
        : threadnum_(threadnum), isstart_(false)
    {
        assert(threadnum > 0);
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&cond_, nullptr);
    }
    void LockQueue()
    {
        pthread_mutex_lock(&mutex_);
    }
    void unLockQueue()
    {
        pthread_mutex_unlock(&mutex_);
    }
    void WaitForTask()
    {
        //注意条件变量一定要加锁了,不然就完犊子了。
        pthread_cond_wait(&cond_, &mutex_);
    }
    void AwakenForThread()
    {
        pthread_cond_signal(&cond_);
    }
    bool HaveTask()
    {
        return !Taskqueue_.empty();
    }

private:
   
    bool isstart_; // 标记第一次创建线程池。
    queue<T> Taskqueue_;//保存任务
    int threadnum_;//线程池线程个数
    pthread_mutex_t mutex_;//互斥锁
    pthread_cond_t cond_;//条件变量

    // 搞一个单例模式。
    static ThreadPool<T> *instance_;
};

template <class T>
ThreadPool<T> *ThreadPool<T>::instance_ = nullptr;

 四,线程池使用

        我们main函数作为一个创建任务的线程就好了,创建好了就加入线程池,线程池自己就执行任务了。

#include <iostream>
#include <ctime>
#include <string>
#include<thread>
#include "Task.hpp"
#include "ThreadPool.hpp"

using namespace std;
string ops = "+-*/%";
int main()
{

    ThreadPool<Task>* dp;
    dp=ThreadPool<Task>::getinstance();
//unique_ptr<ThreadPool<Task> > dp(ThreadPool<Task>::getinstance());
    //启动线程池。
    dp->start();
    // 主进程相当于有一个派发任务的就好了,只管派发任务,人下的不需要管了。

    while (true)
    {
        // 构建一个任务、
        int one = rand() % 50;
        int two = rand() % 50;
        char op = ops[rand() % ops.size()];

        Task t = Task(one, two, op);

        cout << "生产了一个任务:"<<one<<op<<two<<"=?"<<endl;
              dp->push(t);
        sleep(1);
    }

    return 0;
}

 结果展示

五,谢谢各位佬的观看

      感谢各位佬观看,必须单独一章!!!。

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

LYH_1_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值