2017-12-20 3 views
-1

Ich habe eine sehr einfache Producer/Consumer basierend auf C++ 11 Notizen geschrieben und nicht herausfinden, warum die unique_lock() die Sperre nicht freigibt, wenn außerhalb des Bereichs.Schwierigkeit mit unique_lock in einfachen Producer & Consumer

struct Message{ 
Message(int x):data(x){cout<<"+";} 
int data; 
}; 


queue<Message*> g_queue; 
condition_variable cv; 
mutex m; 

void consumer() 
{ 
    do { 
     unique_lock<mutex> lck {m}; 
     cv.wait(lck, [&](){return !g_queue.empty();}); 
     cout<<"Notified..."; 
     auto& obj = g_queue.front(); 
     std::cout<<obj->data; 
     g_queue.pop(); 
     cout<<"."; 
     lck.unlock(); -----(1) 
    } while(1); 

} 

void producer() 
{ 
    while(true){ 
      unique_lock<mutex> lck {m}; 
      Message msg{5}; 
      cout<<"Queue size:"<<g_queue.size()<<'\n'; 
      g_queue.push(&msg); 
      cv.notify_one(); 
      lck.unlock(); -------(2) 
      cout<<"-"<<'\n'; 
      this_thread::sleep_for(std::chrono::milliseconds{2000}); 
    } 
} 

Und verwenden Sie es als: -

thread Q(&consumer); 
    thread P(&producer); 
    P.join(); 
    Q.join(); 

Die Ausgabe lautet: -

+Queue size:0 
-Notified... 
5.+Queue size:0 
-Notified...5 
.+Queue size:0 
-Notified...5 

Technisch gesehen, ja, muss Erzeuger Verbraucher sagen, dass ich bereit bin, und die Bedürfnisse der Verbraucher zu Lassen Sie Producer wissen, senden Sie mehr Daten. Ich bin nicht klar, was zu verwenden ist, tut Zustandsvariable dies oder tut dies unique_lock.

Genau, warum ich brauche (1) und (2), wenn der Umfang können die Sperren freigeben

== == Bearbeiten Es folgt die bearbeitete Code, der gut arbeitet,

void consumer() 
{ 
    do { 
     unique_lock<mutex> lck {m}; 
     cv.wait(lck, [&](){return !g_queue.empty();}); 
     cout<<"Notified..."; 
     auto& obj = g_queue.front(); 
     std::cout<<obj->data; 
     g_queue.pop(); 
     cout<<"."; 
    } while(1); 

} 

Message msg{5}; 
void producer() 
{ 
    while(true){ 
      unique_lock<mutex> lck {m}; 
      cout<<"Queue size:"<<g_queue.size()<<'\n'; 
      g_queue.push(&msg); 
      cv.notify_one(); 
      cout<<"-"<<'\n'; 
    } 
} 

Nun, wie kann ich ein wenig Gas geben, wenn ich in Producer oder Consumer möchte, wenn der Schlaf riskant ist?

+0

Sie brauchen nicht (1). Du brauchst (2) weil sonst 'Produzent' das Schloss während des Schlafes hält und dadurch' Konsumenten' verhungert. Ihr Programm weist auch undefiniertes Verhalten auf: es füllt 'g_queue' mit Zeigern auf lokale Variablen, die sehr gut zerstört werden können, wenn der Zeiger abgerufen und dereferenziert wird. –

+0

ja, ich verstehe es jetzt. Verhungern ist die Ursache für das Verhalten, das ich sehen konnte, als ich mit dem Entfernen von (1) und (2) beide rannte. So entfernte ich die Schlafanweisung und konnte Kontextschalter in der Ausgabe sehen. Außerdem konnte ich das Problem der lokalen Variablen verstehen und habe es auch in meinem Code korrigiert. – cpp11dev

Antwort

0

Nicht sicher, dass es hier noch eine Frage gibt, aber eine Lösung für die Drosselung ist, eine maximale Größe zu haben, zu der die Warteschlange im Produzenten wachsen darf. Wenn die Warteschlange diese Größe erreicht, wartet der Producer auf eine andere Zustandsvariable. Der Verbraucher signalisiert diese zweite Zustandsvariable, wenn die Warteschlange eine bestimmte Größe unterschreitet. (Die letztere Größe ist vielleicht etwas kleiner als das Maximum, um eine Hysterese zu ergeben.) Das Prädikat für das Warten auf diese neue Zustandsvariable ist g_queue.size() >= max_size.

+0

ja das war immer noch eine Frage. Für die Drosselung benötigen wir also zwei neue CVs, um die Leere und Fülle des Warteschlangenpuffers zu verwalten. An diesem Punkt, wenn ich noch nicht über die Optimierung nachdenke, kann ich den obigen Code neu schreiben, um die Drosselung zu verwenden. Lass mich versuchen, wieder hierher zu kommen. Vielen Dank. – cpp11dev