2016-09-14 2 views
11

Ich habe eine Funktion für gelegentlich ein Frame von GigE-Kamera, und will es schnell zurück. Das Standardverfahren ist wie folgt:Std :: Mutex mit RAII, aber fertig und im Hintergrund Thread

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

Return Daten bereit sind nach GetNextFrame() und StopCapture() ist ziemlich langsam; Daher möchte ich so schnell wie möglich img zurückgeben und einen Hintergrund-Thread spawnen, um StopCapture() zu tun. In dem (unwahrscheinlichen) Fall, dass die Akquisition erneut gestartet wird, möchte ich den Zugriff durch einen Mutex schützen. Es gibt Orte, an denen Ausnahmen ausgelöst werden können. Daher entscheide ich mich für eine RAII-Stil-Sperre, die beim Verlassen des Bereichs freigegeben wird. Gleichzeitig muss ich die Sperre in den Hintergrund Thread übertragen. So etwas wie dieser (Pseudo-Code):

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

}; 

Wie kann ich das Schloss vom Anrufer mit dem Hintergrund-Thread hervorgebracht? Oder gibt es einen besseren Weg, damit umzugehen?

EDIT: Ausreichende Lebensdauer von CamIface Instanz ist sichergestellt, bitte nehmen Sie an, es existiert für immer.

+0

Ich würde den Thread an "CamIface" anhängen, anstatt es zu lösen. – Jarod42

+0

http://StackOverflow.com/a/20669290/104774 sollte Ihre Frage beantworten, obwohl ich die Antwort von @PeterT – stefaanv

+3

Ich denke nicht, es ist so einfach wie ein Move Capture. std :: mutex :: unlock muss für denselben Thread aufgerufen werden, für den der Mutex gesperrt war: http://en.cppreference.com/w/cpp/thread/mutex/unlock –

Antwort

1

Die Tatsache, dass dies schwer zu tun ist, sollte darauf hindeuten, dass Ihr Design seltsam asymmetrisch ist. Setzen Sie stattdessen die gesamte Interaktion der Kamera in den Hintergrund-Thread mit allen Mutex-Operationen aus diesem Thread. Stellen Sie sich den Kamera-Thread als Besitzer der Kamera-Ressource und des entsprechenden Mutex vor.

Dann übergeben Sie die erfaßten Rahmen über die Threadgrenze mit einer std :: future oder anderen Synchronisation wie eine gleichzeitige Warteschlange. Sie könnten von hier aus den Hintergrundfaden persistent machen. Beachten Sie, dass dies nicht bedeutet, dass das Capture die ganze Zeit laufen muss, es könnte nur die Thread-Verwaltung erleichtern: Wenn das Kamera-Objekt den Thread besitzt, kann der Destruktor es zum Beenden veranlassen, dann join().

3

Verschieben Sie den std :: unique_lock in den Hintergrundthread.

+0

Ja, das möchte ich machen. Wie? – eudoxos

+3

** Warnung **: wird nicht helfen, wenn die aufrufende CamIFace-Instanz (die den Mutex besitzt) auf einem eigenen Thread den Bereich verlässt und dabei den Mutex zerstört - stellen Sie sicher, dass Sie am Leben erhalten. –

7

Aktualisierte Antwort: @Revolver_Ocelot ist richtig, dass meine Antwort unbestimmte Verhalten fördert, die ich vermeiden möchte.

Also lass mich die einfache Implementierung Semaphore verwenden, um von 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; 
    }; 

}; 

Alter Antwort: Genau wie PanicSheep sagte, den Mutex in das Gewinde bewegen. Zum Beispiel wie folgt aus:

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

auch nur zu bemerken, tun dies nicht:

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

Weil Sie den Faden Inbetriebnahme und den Funktionsumfang Endung sind Rennen.

+3

Mutex-Besitzer ist eine Eigenschaft von Thread. Du ** musst ** Mutex in demselben Thread freischalten, in dem du es erworben hast. [Sonst hast du UB] (http://stackoverflow.com/a/38442900/3410396) –

+1

wahr, ich denke ein Semaphor oder etwas anderes, wäre angemessen – PeterT

+0

@Revolver_Ocelot nochmals vielen Dank für den Kommentar, ich habe eine Version mit einem einfacher Semaphor. (edit: aber ich habe die Ausnahmebedingung vergessen, ich werde darüber nachdenken) – PeterT

3

Sie können sowohl mutex als auch condition_variable verwenden, um die Synchronisation durchzuführen. Außerdem ist es gefährlich, den Hintergrund-Thread zu trennen, da der Thread möglicherweise noch läuft, während das CamIface-Objekt zerstört wurde.

+0

CamIface Lebensdauer ist kein Problem, ich habe das zur Frage hinzugefügt. Was schlägst du vor, einen Thread laufen zu lassen, der die Reinigung die ganze Zeit tut, wartet und tritt ein, wenn das Anhalten wahr ist? Nette Idee, danke! – eudoxos

+0

@eudoxos Ja, es ist nicht nötig, immer wieder einen neuen Thread zu erstellen. Das Erstellen eines Threads kostet auch seinen Preis. –

+0

Ich denke, ich muss eine Art Schleife in 'stop()' setzen, sonst wird es nur einmal aufgerufen. Und dann überprüfen Sie, ob der dtor mit einer anderen Variable aufgerufen wird, um zu wissen, wann Sie zurückkehren müssen? – eudoxos

Verwandte Themen