我目前在加入已完成的std :: thread时遇到问题。 我有一个简单的消费者阶层:
//Consumer.h
class Consumer
{
public:
//Ctor, Dtor
void Start();
void Release();
private:
void Run();
std::atomic<bool> m_terminate;
std::thread m_thread;
};
//Consumer.cpp
void Consumer::Start()
{
m_thread = std::thread(&EncodingProcessor::Run, this);
}
void Consumer::Release()
{
m_terminate = true;
if (m_thread.joinable())
m_thread.join(); //Here is the deadlock
}
void Consumer::Run()
{
while (true)
{
if (m_terminate)
break;
//This queue is blocking, it is fed with events from an encoder
PMVR::HardwareEncoder::EventWithId currentEvent = m_hwEncoder.GetEncodeEvent();
//If there is an event, again, wait for it, until the according frame is encoded
WaitForSingleObject(currentEvent.handle, INFINITE);
//Lock the encoded bitstream
PMVR::HardwareEncoder::BitStreamBufferInfo currentData =
m_hwEncoder.LockBitstream(currentEvent.id);
std::vector<unsigned char> tmp((unsigned char*)currentData.bitstreamPointer,
(unsigned char*)currentData.bitstreamPointer + currentData.bitstreamSize);
//And process it.
m_consumer->ProcessEncodingResult(currentData.bitstreamPointer, currentData.bitstreamSize);
m_hwEncoder.UnlockBitstream(currentEvent.id);
}
}
所以我可以开始这个帖子了。线程做它应该做的事情。我可以结束线程,因此Run()
内的循环会被破坏。但是,如果我想加入该线程,我会遇到死锁。
我们不是在谈论main()
完成后发布的线程。我可以通过按键来Release()
,但不是它可以工作的时间。
修改
Start()
以这种方式调用:
m_processorThread = new Consumer(*m_hwEncoder,
std::make_unique<FileSystemWriter>("file.h264"));
m_processorThread->Start();
Release()
以这种方式调用:
if (glfwGetKey(handler->GetWindow(), GLFW_KEY_M) && !m_pressed)
{
m_pressed = true;
sessionAPI.Close();
}
sessionAPI.close()
只需致电Release()
。没什么。
EDIT2:
对不起,你是对的。到目前为止我发布的代码正在工作......
所以问题似乎在Run()
方法内部(更新,参见上文)。
所以我的误解是,因为在循环的顶部突破,它下面的所有东西都不会被执行......看起来GetEncodeEvent()
会产生死锁。但为什么?是否有一种优雅的方式在线程不等待某事的时候打破整个循环?此外,事件提供者仍然存在,因此应该有通知......
答案 0 :(得分:1)
我认为问题出在这里:
{
if (m_terminate)
break;
//This queue is blocking, it is fed with events from an encoder
PMVR::HardwareEncoder::EventWithId currentEvent = m_hwEncoder.GetEncodeEvent();
//If there is an event, again, wait for it, until the according frame is encoded
WaitForSingleObject(currentEvent.handle, INFINITE);
将m_terminate
设置为true
非常好,但您的主题并未在那里查找。它在WaitForSingleObject
行被阻止。
这是使用std::condition_variable
的好理由。
示例:
#include <condition_variable>
#include <mutex>
#include <thread>
#include <queue>
#include <cassert>
struct some_work {};
struct worker
{
void start()
{
assert(_stopped);
_stopped = false;
// memory fence happened here. The above write is safe
_thread = std::thread(&worker::run, this);
}
void stop()
{
auto lock = std::unique_lock<std::mutex>(_sc_mutex);
// this is a memory fence
assert(!_stopped);
_stopped = true;
// so is this
lock.unlock();
// notify_all, in case someone adds a thread pool and does not look here!
// note: notify *after* we have released the lock.
_state_changed.notify_all();
if (_thread.joinable())
_thread.join();
}
void post_work(some_work w)
{
auto lock = std::unique_lock<std::mutex>(_sc_mutex);
assert(!_stopped);
_workload.push(std::move(w));
lock.unlock();
// only notify one - we only added one piece of work.
_state_changed.notify_one();
}
// allows a monitor to wait until all work is flushed before
// stopping if necessary
void wait()
{
auto lock = std::unique_lock<std::mutex>(_sc_mutex);
_maybe_stop.wait(lock, [this]
{
return should_stop()
or no_more_work();
});
}
private:
void run()
{
std::unique_lock<std::mutex> lock(_sc_mutex);
_state_changed.wait(lock, [this]
{
return this->work_to_do()
or this->should_stop();
});
if (should_stop())
return;
// there is work to do...
auto my_work = std::move(_workload.front());
_workload.pop();
lock.unlock();
// do my work here, once we've locked.
// this is here for the wait() function above.
// if you don't want a wait(), you can dump this
lock.lock();
if (no_more_work() or should_stop())
{
lock.unlock();
_maybe_stop.notify_all();
}
}
bool work_to_do() const { return not _workload.empty(); }
bool no_more_work() const { return _workload.empty(); }
bool should_stop() const { return _stopped; }
std::mutex _sc_mutex;
std::condition_variable _state_changed;
std::condition_variable _maybe_stop;
std::queue<some_work> _workload;
std::thread _thread;
bool _stopped = true;
};
int main()
{
worker w;
w.start();
w.post_work(some_work());
w.post_work(some_work());
w.post_work(some_work());
w.post_work(some_work());
w.post_work(some_work());
w.post_work(some_work());
// do we want to ensure that all the work is done?
w.wait();
w.stop();
}
答案 1 :(得分:1)
您的代码表明GetEncodeEvent
正在阻止。如果这是真的,那么您的代码可以无限期地坐在该行代码中,而不会看到m_terminate
中的更改。随后,代码可以在WaitForSingleObject
无限期地停留。
您可能需要考虑在整个功能中测试m_terminate
。
你不能打断WaitForSingleObject
但是你可以指定一个超时并简单地将它包装在一个循环中
for (;;) {
if (m_terminate)
return;
auto res = WaitForSingleObject(currentEvent.handle, 20);
switch (res) { // check the return value
case WAIT_TIMEOUT: continue;
case WAIT_OBJECT_0: break;
default: error(...);
}
}
您的另一个选择是为线程创建WaitEvent
并使用WaitForMultipleObjects
两个句柄并使用SetEvent
中的Consumer::Release
来通知线程。