2016-09-14 62 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