写了一段高端C++代码

2021-07-09 16:06:25 浏览数 (1)

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

背景:

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

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

代码语言:javascript复制
void func() {
    task1();
    task2(); // 需要在GL线程执行
    task3();
}

分析:

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

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

代码语言:javascript复制
void task2() {
    ...
    notify();
}
void func() {
    task1();
    task2(); // 需要在GL线程执行
    wait();
    task3();
}

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

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

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

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

方案:

话不多说,直接上代码:‍

代码语言:javascript复制
#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 进阶独家学习资料。

0 人点赞