2016-09-14 39 views
11

我有一个偶尔从GigE相机获取帧,并希望它快速返回的功能。标准程序是这样的:GetNextFrame()std ::互斥与RAII但在后台线程完成和释放

// ... 
camera.StartCapture(); 
Image img=camera.GetNextFrame(); 
camera.StopCapture(); // <-- takes a few secs 
return img; 

返回数据已准备就绪,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

我会将该线程附加到'CamIface'而不是分离它。 – Jarod42

+0

http://stackoverflow.com/a/20669290/104774应该回答你的问题,虽然我更喜欢@PeterT的回答 – stefaanv

+3

我不认为它像移动捕捉一样简单。 std :: mutex :: unlock必须在该互斥锁被锁定的同一线程上调用:http://en.cppreference.com/w/cpp/thread/mutex/unlock –

回答

1

这很难做到的事实表明您的设计奇怪地不对称。相反,将所有相机交互放置在后台线程中,并使用该线程中的所有互斥操作。将相机线程想象成拥有相机资源和相应的互斥体。

然后通过std :: future或其他同步(如并发队列)在线程边界上传递捕获的帧。你可以从这里考虑使后台线程持久。请注意,这并不意味着捕获必须一直运行,它可能只是简化线程管理:如果相机对象拥有该线程,析构函数可以发信号通知它退出,然后join()它。

3

将std :: unique_lock移动到后台线程。

+0

是的,这就是我想要做的。怎么样? – eudoxos

+3

**警告**:如果调用CamIFace实例(它拥有互斥锁)超出了它自己的线程范围而破坏了进程中的互斥锁 - 请确保保持它的活力。 –

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 
}); 

因为你赛车线程启动和结束的功能范围。

+3

互斥所有权是线程的一个属性。你**必须**解锁互斥你在同一线程中获得它。 [否则你有UB](http://stackoverflow.com/a/38442900/3410396) –

+1

真的,我猜信号量或别的东西,将是适当的 – PeterT

+0

@Revolver_Ocelot再次感谢评论,我添加了一个版本使用简单的信号量。 (编辑:但我忘记了例外要求,我将会看到关于这个的) – PeterT

3

您可以同时使用mutexcondition_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; 
}; 
+0

CamIface使用寿命不是问题,我在问题中加入了这个问题。你提出的建议是一直运行一个线程进行清理,等待,并在任何时候停止是真的踢?好主意,谢谢! – eudoxos

+0

@eudoxos是的,没有必要一次又一次地创建一个新的线程。创建线程也需要它的代价。 –

+0

我想我必须把某种循环放到'stop()'中,否则它只会被调用一次。然后检查是否使用其他变量调用了dtor,知道何时返回? – eudoxos