跨平台线程池

线程的创建和销毁需要消耗操作系统的资源,如果频繁创建和销毁线程,会产生大量额外开销,甚至可能成为性能瓶颈。线程池中的核心线程通常是预先创建并存活的,当任务到达时,无需等待线程创建即可立即执行,减少了任务的等待时间,提升了系统的响应性。

目录

1 原代码

2 ThreadPoll(int numthread)

3 void enqueue(F &&f,Args&&... args)

4 ~ThreadPoll()


1 原代码

#include <iostream>
#include <string>

#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <vector>
#include <functional>

class ThreadPoll
{
private:
    std::vector<std::thread>theads;//线程数组
    std::queue <std::function<void()>> tasks;//任务队列
    std::mutex mutex1;//创建互斥锁
    std::condition_variable condition;//条件变量,用于通知线程
    bool is_stop;//线程池终止
public:
    ThreadPoll(int numthread);
    ~ThreadPoll();

    //入队函数,F函数,Args:参数
    template<class F,class... Args>
    void enqueue(F &&f,Args&&... args)
    {
        //传入线程函数
        std::function<void()> task = 
            std::bind(std::forward<F>(f),std::forward<Args>(args)...);

            {
                //将函数加入tasks队列
                std::unique_lock<std::mutex> lock(mutex1);
                tasks.emplace(std::move(task));//入队
            }
            condition.notify_one();//通知一下
    }
};



ThreadPoll::ThreadPoll(int numthread):is_stop(false)
{
    for (int i = 0;i < numthread;i++)
    {
        theads.emplace_back([this]()
        {
            while(1)
            {
                std::unique_lock<std::mutex> lock(mutex1);
                //非阻塞条件:1.任务为非空,2.线程终止
                condition.wait(lock,[this](){return !tasks.empty() || is_stop;});
                //1.进程要结束了-->触发stop,2.所有线程都已入队且结束-->都成立--->线程可终止
                if (is_stop && tasks.empty()) return;
                //取任务
                std::function<void()> task(std::move(tasks.front()));
                tasks.pop();
                lock.unlock();//启动任务前解锁
                task();
            }
        });
    }
}
ThreadPoll::~ThreadPoll()
{
    {
        std::unique_lock<std::mutex> lock(mutex1);
        is_stop = true;        
    }
    //通知所有任务可以结束了
    condition.notify_all();//通知
    for (auto &thread: theads)
        thread.join();
}


int main(int argc,char** argv)
{
    ThreadPoll poll(4);
    std::mutex cout_mutex;
    for (int i = 0;i < 10;i++)
        poll.enqueue([i,&cout_mutex](){
                std::unique_lock<std::mutex> lock(cout_mutex);
                std::cout << "线程" << i << "正在运行" << std::endl;
        });
    return 0;
}

std::vector<std::thread>theads;

创建一个存放线程的容器theads

std::queue <std::function<void()>> tasks;

创建一个队列容器,用以存放函数

std::mutex mutex1;

创建互斥锁,用以处理线程竞态

std::condition_variable condition;

创建条件变量,用以处理线程阻塞与唤醒

is_stop

解析信号,触发解析时,置1

2 ThreadPoll(int numthread)

ThreadPoll::ThreadPoll(int numthread):is_stop(false)
{
    for (int i = 0;i < numthread;i++)
    {
        theads.emplace_back([this]()
        {
            while(1)
            {
                std::unique_lock<std::mutex> lock(mutex1);
                //非阻塞条件:1.任务为非空,2.线程终止
                condition.wait(lock,[this](){return !tasks.empty() || is_stop;});
                //1.进程要结束了-->触发stop,2.所有线程都已入队且结束-->都成立--->线程可终止
                if (is_stop && tasks.empty()) return;
                //取任务
                std::function<void()> task(std::move(tasks.front()));
                tasks.pop();
                lock.unlock();//启动任务前解锁
                task();
            }
        });
    }
}

在构造函数中,创建出numthread个线程,每个线程执行lambda表达式

lambda表达式中,如果任务队列tasks不为空,或者析构信号is_stop为真,则条件变量condition不阻塞。阻塞时解锁。

队列为空,且析构信号为真,则停止线程。

通过move()移动构造,转移函数所有权,避免额外的内存开销。

在线程执行出队之前,上锁保护线程,避免其他线程从任务重复获队列函数造成冲突。

出队并解锁,执行任务函数。


3 void enqueue(F &&f,Args&&... args)

    template<class F,class... Args>
    void enqueue(F &&f,Args&&... args)
    {
        //传入线程函数
        std::function<void()> task = 
            std::bind(std::forward<F>(f),std::forward<Args>(args)...);

            {
                //将函数加入tasks队列
                std::unique_lock<std::mutex> lock(mutex1);
                tasks.emplace(std::move(task));//入队
            }
            condition.notify_one();//通知一下
    }

通过 万能引用&& 传入任意类型的可变参数据Args,以完美转发forward()保留参数原本的性质避免在万能引用的过程中参数发生语义转化。

将自定义的函数f与参数args传入到队列tasks中,notify_one()通知一个等待中的线程解除阻塞


~ThreadPoll()

ThreadPoll::~ThreadPoll()
{
    {
        std::unique_lock<std::mutex> lock(mutex1);
        is_stop = true;        
    }
    //通知所有任务可以结束了
    condition.notify_all();//通知
    for (auto &thread: theads)
        thread.join();
}

将析构信号is_stop置1,notify_all()唤醒所有阻塞中的线程,等待线程停止。

收到信号后,线程不再阻塞,无任务时停止。


int main(int argc,char** argv)
{
    ThreadPoll poll(4);
    std::mutex cout_mutex;
    for (int i = 0;i < 10;i++)
        poll.enqueue([i,&cout_mutex](){
                std::unique_lock<std::mutex> lock(cout_mutex);
                std::cout << "线程" << i << "正在运行" << std::endl;
        });
    return 0;
}

在这段代码中,创建10个任务被放入队列中,被取出读取执行,最多同时4个线程运行,主函数触发return 0后触发析构,等待线程结束后,退出。

至此,线程池结构完成。如果这篇文章对你有帮助的话,请点个赞吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值