查看原文
其他

写了一段高端C++代码

程序喵大人 程序喵大人 2022-08-22

大家好,我是程序喵。没有“想你的夜”,只有膨胀的“Yeah”!最近写了一段有意思的代码,来分享给大家.



背景:

在音视频方向中,线程分为普通线程和GL线程(OpenGL线程),GL线程中可以执行OpenGL相关的语句,做一些图像渲染的工作,也可以理解为所有GL语句都要在GL线程中执行;而在普通线程中,只能执行那些我们平时经常接触的普通语句。


在具体项目开发中会有些需求:在普通线程中突然想要执行某些必须要在GL线程下执行的任务(比如某些初始化工作,释放某些GL相关的对象),执行完此任务后又继续执行自己的任务,像在同一个线程执行一样:


void func() { task1(); task2(); // 需要在GL线程执行 task3();}


分析:

这里有个关键点:task3()一定要等到task2()执行完毕后才可执行,但是由于task2()是被抛到了其他线程运行,没有起到阻塞执行的效果。


怎么能达到目的呢?可以这样使用条件变量:

void task2() { ... notify();}void func() { task1(); task2(); // 需要在GL线程执行 wait(); task3();}

普通线程在task2()后使用wait()阻塞线程,待GL线程中的任务执行完后使用notity()打断普通线程的阻塞,可达到顺序执行的目的。


但这样非常麻烦,而且不通用,代码还相当难看。



这里其实可以使用C++11中的future,通过future可以达到阻塞线程的目的,而且还可以获取函数执行的结果。


关于future的具体用法可以看我之前的文章:《c++11新特性之线程相关所有知识点



方案:

话不多说,直接上代码:

#include <atomic>#include <chrono>#include <condition_variable>#include <functional>#include <future>#include <iostream>#include <memory>#include <mutex>#include <queue>#include <thread>#include <vector>
class GLTaskDispatch{   public:    static GLTaskDispatch &getInstance()    {        static GLTaskDispatch t;        return t;    }
    bool start()    {        auto func = [this]() {            while (!_interrupt.load()) {                std::function<void()> task;                {                    std::unique_lock<std::mutex> lock(this->_taskMutex);                    this->_taskCv.wait(lock, [this] {                        return this->_interrupt.load() || !this->_tasks.empty();                    });                    if (this->_interrupt.load()) {                        continue;                    }                    task = std::move(this->_tasks.front());                    this->_tasks.pop();                }                task();            }        };        _thread = std::make_unique<std::thread>(func);        return true;    }
    bool stop()    {        _interrupt.store(true);        this->_taskCv.notify_all();        if (_thread && _thread->joinable()) {            _thread->join();        }        return true;    }
    template <typename F, typename... Args>    auto run(F &&f, Args &&... args)        -> std::shared_ptr<std::future<std::result_of_t<F(Args...)>>>    {        using returnType = std::result_of_t<F(Args...)>;        auto task = std::make_shared<std::packaged_task<returnType()>>(            std::bind(std::forward<F>(f), std::forward<Args>(args)...));
        std::future<returnType> ret = task->get_future();        {            std::lock_guard<std::mutex> lock(this->_taskMutex);            this->_tasks.emplace([task]() { (*task)(); });        }        this->_taskCv.notify_all();        return std::make_shared<std::future<std::result_of_t<F(Args...)>>>(            std::move(ret));    }
   private:    GLTaskDispatch() {}    std::unique_ptr<std::thread> _thread = nullptr;    std::atomic<bool> _interrupt{false};    std::queue<std::function<void()>> _tasks;    std::mutex _taskMutex;    std::condition_variable _taskCv;};
void func1(){    for (int i = 0; i < 20; i++) {        std::cout << "func1 " << i << "\n";    }}
int func2(){    for (int i = 0; i < 20; i++) {        std::cout << "func2 " << i << "\n";    }    return 39;}
int main(){    GLTaskDispatch &t = GLTaskDispatch::getInstance();    t.start();    t.run(func1)->get();    std::cout << "func1 return \ n";    int d = t.run(func2)->get();    std::cout << "return " << d << std::endl;    std::this_thread::sleep_for(std::chrono::seconds(2));    t.stop();    return 0;}


注意:

这里run()函数返回的是智能指针,所以使用get()阻塞执行获取结果的方法应该使用->,而不是.方式。


打完收工。


C++学习资料免费获取方法:关注程序喵大人,后台回复“程序喵”即可免费获取40万字C++进阶独家学习资料。





往期推荐


1、少写点
if-else吧,它的效率有多低你知道吗?
2、年度原创好文汇总
3、全网首发!!C++20新特性全在这一张图里了
4
他来了,他来了,C+
+17新特性精华都在这了
5、一文让你搞懂设计模式
6、C++11新特性,所有知识点都在这了!


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存