大家好,我是程序喵。没有“想你的夜”,只有膨胀的“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 进阶独家学习资料。