我有一个偶尔从GigE相机获取帧的功能,并希望它快速返回。标准程序是这样的:
// ...
camera.StartCapture();
Image img=camera.GetNextFrame();
camera.StopCapture(); // <-- takes a few secs
return img;
GetNextFrame()
之后返回数据准备就绪,StopCapture()
非常慢;因此,我想尽快返回img
并生成一个后台线程来执行StopCapture()
。但是,在(不太可能)再次启动采集的情况下,我希望通过互斥锁保护访问。有些地方可以抛出异常,所以我决定使用RAII风格的锁,它将在范围退出时释放。同时,我需要将锁转移到后台线程。像这样的东西(伪代码):
class CamIface{
std::mutex mutex;
CameraHw camera;
public:
Image acquire(){
std::unique_lock<std::mutex> lock(mutex); // waits for cleanup after the previous call to finish
camera.StartCapture();
Image img=camera.GetNextFrame();
std::thread bg([&]{
camera.StopCapture(); // takes a long time
lock.release(); // release the lock here, somehow
});
bg.detach();
return img;
// do not destroy&release lock here, do it in the bg thread
};
};
如何将锁从调用者转移到生成的后台线程?或者有更好的方法来处理这个问题吗?
编辑:保证CamIface
个实例有足够的生命周期,请假设它永远存在。
答案 0 :(得分:7)
更新答案: @Revolver_Ocelot是正确的,我的回答鼓励未定义的行为,我想避免。
因此,让我使用this SO Answer
中的简单信号量实现#include <mutex>
#include <thread>
#include <condition_variable>
class Semaphore {
public:
Semaphore (int count_ = 0)
: count(count_) {}
inline void notify()
{
std::unique_lock<std::mutex> lock(mtx);
count++;
cv.notify_one();
}
inline void wait()
{
std::unique_lock<std::mutex> lock(mtx);
while(count == 0){
cv.wait(lock);
}
count--;
}
private:
std::mutex mtx;
std::condition_variable cv;
int count;
};
class SemGuard
{
Semaphore* sem;
public:
SemGuard(Semaphore& semaphore) : sem(&semaphore)
{
sem->wait();
}
~SemGuard()
{
if (sem)sem->notify();
}
SemGuard(const SemGuard& other) = delete;
SemGuard& operator=(const SemGuard& other) = delete;
SemGuard(SemGuard&& other) : sem(other.sem)
{
other.sem = nullptr;
}
SemGuard& operator=(SemGuard&& other)
{
if (sem)sem->notify();
sem = other.sem;
other.sem = nullptr;
return *this;
}
};
class CamIface{
Semaphore sem;
CameraHw camera;
public:
CamIface() : sem(1){}
Image acquire(){
SemGuard guard(sem);
camera.StartCapture();
Image img=camera.GetNextFrame();
std::thread bg([&](SemGuard guard){
camera.StopCapture(); // takes a long time
}, std::move(guard));
bg.detach();
return img;
};
};
旧答案: 就像PanicSheep所说的那样,将互斥锁移动到线程中。例如:
std::mutex mut;
void func()
{
std::unique_lock<std::mutex> lock(mut);
std::thread bg([&](std::unique_lock<std::mutex> lock)
{
camera.StopCapture(); // takes a long time
},std::move(lock));
bg.detach();
}
另外,请注意,不要这样做:
std::thread bg([&]()
{
std::unique_lock<std::mutex> local_lock = std::move(lock);
camera.StopCapture(); // takes a long time
local_lock.release(); // release the lock here, somehow
});
因为您正在参与线程启动和功能范围的结束。
答案 1 :(得分:3)
将std :: unique_lock移动到后台线程。
答案 2 :(得分:3)
您可以同时使用mutex
和condition_variable
进行同步。分离后台线程也很危险,因为在CamIface对象被破坏时线程可能仍在运行。
class CamIface {
public:
CamIface() {
background_thread = std::thread(&CamIface::stop, this);
}
~CamIface() {
if (background_thread.joinable()) {
exit = true;
cv.notify_all();
background_thread.join();
}
}
Image acquire() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this]() { return !this->stopping; });
// acquire your image here...
stopping = true;
cv.notify_all();
return img;
}
private:
void stop() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this]() { return this->stopping || this->exit; });
if (exit) return; // exit if needed.
camera.StopCapture();
stopping = false;
cv.notify_one();
}
}
std::mutex mtx;
std::condition_variable cv;
atomic<bool> stopping = {false};
atomic<bool> exit = {false};
CameraHw camera;
std::thread background_thread;
};
答案 3 :(得分:1)
这很难做到的事实应该表明你的设计是奇怪的不对称。相反,将所有相机交互放在后台线程中,并使用该线程中的所有互斥操作。将相机线程视为拥有相机资源和相应的互斥锁。
然后使用std :: future或其他同步(如并发队列)跨线程边界传递捕获的帧。您可以从这里考虑使后台线程持久化。请注意,这并不意味着捕获必须始终运行,它可能只是使线程管理更容易:如果摄像机对象拥有线程,析构函数可以发出信号退出,然后join()
它。