栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++ 多线程学习12 线程池

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++ 多线程学习12 线程池

一、启动线程池

mian()函数中先创建线程池对象:

XThreadPool pool;

调用线程的初始化函数init(),向init()传参数n来为线程池的线程数量成员赋值:

this->thread_num_ = num;

用start()启动线程,在run中先判断线程池是否完成初始化,再判断线程是否以及启动了:

然后创建thread_num_个线程,每个线程的线程入口为run()

 auto th = make_shared(&XThreadPool::Run, this);
 threads_.push_back(th);

为指向对象的类型,新建一个thread对象需要一个入口函数,由于这个函数是类成员函数,所以还需要一个this指针,返回类型为一个指针th,随后用一个线程列表threads_来管理这个th:

std::vector< std::shared_ptr > threads_;

此时线程池已经启动了:

二、线程池开始服务于用户

main()在启动线程后紧跟一个无限循环,与用户在终端上进行交互,
(1)输入L时调用线程池的task_run_count(),返回任务计数,任务计数在task run前+1,结束时减1,
(2)输入e时直接break结束无限循环
(3)输入v时开始视频转码:先用make_shared来创建一个task对象,输入视频源,输出尺寸,保存信息等。将这些信息丢给创建好的task对象,然后将这个任务丢到线程池中,用线程池来判断任务的启动时机

//线程池线程的入口函数
void XThreadPool::Run()
{
    cout << "begin XThreadPool Run " << this_thread::get_id() << endl;
    while (!is_exit())
    {
        auto task = GetTask();
        if (!task)continue;
        ++task_run_count_;
        try
        {
            auto re = task->Run();
            //task->SetValue(re);
        }
        catch (...)
        {

        }
        --task_run_count_;
    }

    cout << "end XThreadPool Run " << this_thread::get_id() << endl;
}

线程池中的线程本身无任务,每个线程在while()中不断重复三件事
(第一件事)通过GetTask()来获得线程池中任务列表tasks_中的task:

unique_lock lock(mux_);
    if (tasks_.empty())
    {
        cv_.wait(lock);
    }
    if (is_exit())
        return nullptr;
    if (tasks_.empty())
        return nullptr;
    auto task = tasks_.front();
    tasks_.pop_front();
    return task;

用户可能大批量输入任务,若没有使用多线程则一次只能处理一个,效率非常低下,又不想无限制创建线程来处理他们,这可能导致系统崩溃,于是使用线程池规定一次只能处理16个任务,未处理的任务在任务队列tasks_中排队,当任务队列为空时,将一直阻塞在cv_.wati(lock)处,直到用户在终端处输入任务后通过cv.notify才会运行后面的
(第二件事)调用刚刚获得的任务的run(),在其中将用户在终端输入的指令组合起来成一个能ffmpeg的命令行,比如:
ffmpeg -y -i test.mp4 -s 400x300 400.mp4 >log.txt 2>&1,然后用system()中直接进行系统调用:

int XVideoTask::Run()
{
    //ffmpeg -y -i test.mp4 -s 400x300 400.mp4 >log.txt 2>&1
    stringstream ss;
    ss << "ffmpeg.exe -y -i " << in_path<<" ";
    if (width > 0 && height > 0)
        ss << " -s " << width << "x" << height<<" ";
    ss << out_path;
    ss << " >" << this_thread::get_id() << ".txt 2>&1";
    return system(ss.str().c_str());
}
三、线程池退出

当输入e退出后,调用线程池的stop()

/// 线程池退出
void XThreadPool::Stop()
{
    is_exit_ = true;
    cv_.notify_all();
    for (auto& th : threads_)
    {
        th->join();
    }
    unique_lock lock(mux_);
    threads_.clear();
}

注意点:
结合线程入口函数去理解:

//线程池线程的入口函数
void XThreadPool::Run()
{
    cout << "begin XThreadPool Run " << this_thread::get_id() << endl;
    while (!is_exit())
    {
        auto task = GetTask();
        if (!task)continue;
        ++task_run_count_;
        try
        {
            auto re = task->Run();
            //task->SetValue(re);
        }
        catch (...)
        {

        }
        --task_run_count_;
    }
}

01设置is_exit_ = true;让线程的run()停掉无限循环
02为啥要cv_.notify_all();
若没有这句,退出时将卡在这:

就算没有任务进来但此时线程池中的16个线程正在run,GetTask()中卡在cv.wait(),其等待cv发给他信号,这个时候这个线程将一直保持阻塞态,所以会卡在这里
03为啥要让所以的线程join()
当一个线程是joinable时调用析构将会报错(当其是可加入时即使其运行完了,其线程资源释放需要其他线程通过join()来完成)

四、跑起来

先开启16个线程,后面是其线程号

输入一次转码命令,然后不停查看当前运行任务个数,先是1,运行结束后再l显示0

由于测试视频较短加上手速较慢,显示不出2:

补充一哈make_shared

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1033267.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号