void VideoRender::execute(){
for(int i = 0; i < 1000; i++)
udpateData(myPath, myIndex);
}
void VideoRender::updateData(const std::string &i_obj_path, const uint i_subIndex)
{
std::shared_ptr<FrameManager> container = std::make_shared<FrameManager>(m_nativeCodec);
std::thread th1(&VideoRender::loadGeometry, this, i_obj_path.c_str(), i_subIndex, container);
std::thread th2(&VideoRender::loadTextures, this, container);
th1.join();
th2.join();
m_fifo.enqueue(container);
}
这里的问题是,每次调用updateData
方法时,都会创建2个新线程。
因此,我决定将此逻辑更改为使用std::condition_variable
我做了什么
1)创建了ConditionEvent
类
h.file
namespace hello_ar
{
class ConditionEvent
{
public:
ConditionEvent() = default;
~ConditionEvent() = default;
void wait();
void notify();
private:
mutable std::mutex m_mutex;
std::condition_variable m_condition;
};
}
cc文件
#include "ConditionEvent.h"
#include <android/log.h>
namespace hello_ar
{
void ConditionEvent::wait()
{
std::unique_lock<std::mutex> lock(m_mutex);
m_condition.wait(lock);
}
void ConditionEvent::notify()
{
std::lock_guard<std::mutex> lock(m_mutex);
m_condition.notify_all();
}
}
然后我创建了一个UploadLooper
类
h文件
namespace hello_ar
{
class UploadLooper
{
public:
UploadLooper() = default;
~UploadLooper();
void start();
void enqueueLoadTextureTask(const std::shared_ptr<FrameManager> &container, std::shared_ptr<ConditionEvent> condition);
void enqueueLoadGeometryTask(const std::shared_ptr<FrameManager> &container, char const *filename, const uint subIndex,
std::shared_ptr<ConditionEvent> condition);
void quit();
private:
enum class Task
{
LoadGeometry, LoadTexture, ERROR
};
struct LooperMessage
{
std::shared_ptr<FrameManager> m_container;
std::shared_ptr<ConditionEvent> m_condition;
char const *m_filename;
uint m_subIndex;
Task m_task;
//Load Geometry
LooperMessage(std::shared_ptr<FrameManager> container, //
std::shared_ptr<ConditionEvent> condition,//
char const *filename = "", //
const uint subIndex = static_cast<const uint>(-1),//
Task task = Task::ERROR //
) : m_container(container), m_condition(condition),
m_filename(filename), m_subIndex(subIndex), m_task(task)
{
}
//Load Textures
LooperMessage(std::shared_ptr<FrameManager> container, //
std::shared_ptr<ConditionEvent> condition,//
Task task = Task::ERROR//
) : LooperMessage(container, //
condition,
"", //
static_cast<uint>(-1), //
task//
)
{
}
};
safe_queue<std::shared_ptr<LooperMessage>> m_fifo;
std::thread m_worker;
void loop();
void trampoline(void *p);
void releaseWorker();
};
}
cc文件
namespace hello_ar
{
UploadLooper::~UploadLooper()
{
quit();
}
void UploadLooper::releaseWorker()
{
if (m_worker.joinable())
m_worker.join();
}
void UploadLooper::trampoline(void *p)
{
((UploadLooper *) p)->loop();
}
void UploadLooper::loop()
{
while (true)
{
if (m_fifo.empty())
continue;
std::shared_ptr<LooperMessage> msg = m_fifo.dequeue();
if (!msg)
{
return;
}
switch (msg->m_task)
{
case Task::LoadGeometry:
{
msg->m_container->LoadFrameData(msg->m_filename, msg->m_subIndex);
msg->m_condition->notify();
}
break;
case Task::LoadTexture:
{
msg->m_container->LoadImage();
msg->m_condition->notify();
}
break;
case Task::ERROR:
break;
}
std::this_thread::yield();
}
}
void UploadLooper::enqueueLoadTextureTask(const std::shared_ptr<FrameManager> &container, std::shared_ptr<ConditionEvent> condition)
{
std::shared_ptr<LooperMessage> msg = std::make_shared<LooperMessage>(container, condition, Task::LoadTexture);
m_fifo.enqueue(msg);
}
void UploadLooper::enqueueLoadGeometryTask(const std::shared_ptr<FrameManager> &container, //
char const *filename, const uint subIndex, //
std::shared_ptr<ConditionEvent> condition)
{
std::shared_ptr<LooperMessage> msg = std::make_shared<LooperMessage>(container, condition, filename, subIndex, Task::LoadGeometry);
m_fifo.enqueue(msg);
}
void UploadLooper::quit()
{
m_fifo.enqueue(nullptr);
releaseWorker();
}
void UploadLooper::start()
{
if (!m_worker.joinable())
{
std::thread t(&UploadLooper::trampoline, this, this);
m_worker = std::move(t);
}
}
}
最终我的乞求实现看起来像这样
void VideoRender::execute(){
for(int i = 0; i < 1000; i++)
udpateData(myPath, myIndex);
}
void VideoRender::updateData(const std::string &i_obj_path, const uint i_subIndex)
{
std::shared_ptr<FrameManager> container = std::make_shared<FrameManager>(m_nativeCodec);
std::shared_ptr<ConditionEvent> texCond = std::make_shared<ConditionEvent>();
std::shared_ptr<ConditionEvent> geoCond = std::make_shared<ConditionEvent>();
m_texLopper.enqueueLoadTextureTask(container, texCond);
m_geometryLopper.enqueueLoadGeometryTask(container, i_obj_path.c_str(), i_subIndex, geoCond);
texCond->wait();
geoCond->wait();
m_fifo.enqueue(container);
}
但是在调试之后,我发现第一次调用updateData
方法之后,我来到m_texLopper
调用notify
,然后来到m_geometryLooper
调用{{1} },然后我来到notify
...尽管循环程序在单独的线程中运行...
我在做什么错了?
编辑
问题是-无法在texCond->wait()
之前调用notify
。由于根据实现,我将任务推送到循环程序(任务执行时间为30毫秒),下一行为wait
。因此,我将任务推入单独的线程-> 30毫秒后wait
->下一行wait
...但它的工作原理类似于推任务-> 30毫秒后通知-> notify
...怎么可能?
答案 0 :(得分:2)
在notify()
之前调用wait()
并非不可能。使用多个线程时,它们的执行可以随时启动和停止。您的任务执行得非常快,因此第一个线程可能在第二个线程完成之前不继续执行是合理的。
您期望这样:
Thread 1 Thread 2
enqueue
wait dequeue
LoadFrameData()
notify
但这也是可能的:
Thread 1 Thread 2
enqueue
dequeue
LoadFrameData()
notify
wait
无论何时通知您的条件变量,您都应添加条件以进行检查。整个代码可以这样简化:
class ConditionEvent {
public:
void ConditionEvent::wait() {
std::unique_lock<std::mutex> lock(m_mutex);
m_condition.wait(lock, [&]() {return notified;});
}
void ConditionEvent::set() {
std::lock_guard<std::mutex> lock(m_mutex);
notified = true;
m_condition.notify_all();
}
void reset() {
notified = false;
}
private:
mutable std::mutex m_mutex;
bool notified = false;
std::condition_variable m_condition;
};
void VideoRender::execute() {
std::shared_ptr<FrameManager> container;
ConditionEvent geometry, textures;
// If this thread obtains the lock, initialize the container
auto init = [&]() {
std::lock_guard<std::mutex> lock(containerMutex);
if (!container) {
container = std::make_shared<FrameManager>(m_nativeCodec);
}
};
// If this thread obtains the lock, enqueue the container
auto enqueue = [&]() {
std::lock_guard<std::mutex> lock(containerMutex);
if (container) {
m_fifo.enqueue(container);
container.reset();
geometry.reset();
textures.reset();
}
};
std::thread t1([]() {
for (int i = 0; i < 1000; i++) {
init();
loadGeometry();
geometry.set();
textures.wait();
enqueue();
}
});
std::thread t2([]() {
for (int i = 0; i < 1000; i++) {
init();
loadTextures();
textures.set();
geometry.wait();
enqueue();
}
});
t1.join();
t2.join();
}
答案 1 :(得分:2)
您需要做的就是向ConditionEvent
添加变量,即
bool notified = false;
然后使用此变量:
void ConditionEvent::wait()
{
std::unique_lock<std::mutex> lock(m_mutex);
m_condition.wait(lock, [this]() {return notified;});
}
void ConditionEvent::notify()
{
std::lock_guard<std::mutex> lock(m_mutex);
notified = true;
m_condition.notify_all();
}
编辑:固定了lambda。