std::mutex with RAII but finish & release in background thread
Updated Answer:@Revolver_Ocelot is right that my answer encourages undefined behavior, which I'd like to avoid.
So let me use the simple Semaphore implementation from 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; };};
Old Answer:Just like PanicSheep said, move the mutex into the thread. For example like this:
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();}
Also, just to remark, don't do this:
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});
Because you're racing the thread startup and the function scope ending.
You can use both mutex
and condition_variable
to do the synchronization. Also it's dangerous to detach the background thread, since the thread might still running while the CamIface object has been destructed.
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;};