2016-07-26 28 views
0

Hallo SO! Gibt es bei jeder Änderung eine Möglichkeit, dass der "wartende Thread" den "Buff" nicht aktualisiert bekommt? sollte ich einen lock_guard in den thread einfügen, der den "buff" ändert? Ich habe über Speicher Zäune auf einige Artikel gelesen, wie diese: https://bartoszmilewski.com/2008/11/05/who-ordered-memory-fences-on-an-x86/ und ich bin nur besorgt darüber. Vielen Dank im Voraus ...kann ein geteilter Puffer threadsicher mit std :: atomic flags sein? Gibt es einen effizienteren Weg dies zu tun?

#include <iostream> 
#include <thread> 
#include <chrono> 
#include <atomic> 

int main(){ 
    std::atomic<bool> ready(false); 
    char buff[20]= "hello"; 

    std::cout << "creating thread to change \"buff\"..." << std::endl; 
    std::thread tr(
     [ & ready, & buff ](){ 
      std::this_thread::sleep_for(std::chrono::milliseconds(100)); 
      //should i put a lock_guard here? 
      buff[0]= 'w'; buff[1]= 'o'; 
      buff[2]= 'r'; buff[3]= 'l'; 
      buff[4]= 'd'; buff[5]= '\0'; 
      std::cout << "buffer changed! sending signal to waiting thread..." << std::endl; 
      ready= true; 
    }); 

    tr.detach(); 
    std::cout << "...waiting for the change..." << std::endl; 
    while(!ready); 

    std::cout << "...got it! buff=" << buff << std::endl; 
} 
+1

Ihr Programm weist ein undefiniertes Verhalten auf, indem es 'tr.join()' oder 'tr.detach()' nicht aufruft, bevor 'tr' den Gültigkeitsbereich verlässt. Außerdem würde das Ersetzen der Besetztzeichen "while (! Ready);" mit "tr.join()" den gleichen Nettoeffekt haben, aber viel effizienter sein. –

+0

danke, du hast Recht ... ich habe es bearbeitet und die detach() jetzt ... ich bin nicht daran interessiert, die Join, ich möchte den Thread unabhängig voneinander laufen – enger

Antwort

1

Weil Sie nicht die Speicher Sichtbarkeit Semantik für Ihre Zuordnung zu ready angegeben haben, können Sie die Standard-Semantik erhalten. Welche sind:

Das Standardverhalten aller atomaren Operationen in der Bibliothek sorgt für sequenziell konsistente Reihenfolge (siehe unten). - std::memory_order

So erhalten Sie "sequenziell konsistente Bestellung". Nun, was zum Teufel ist das?

Jede Operation mit dieser Speicherreihenfolge ist sowohl eine Erfassungsoperation als auch eine Freigabeoperation. Außerdem existiert eine einzige Gesamtbestellung, in der alle Threads alle Änderungen (siehe unten) in derselben Reihenfolge beobachten.

Das klingt gut. Aber was ist eine "Freigabeoperation"?

Ein Speichervorgang mit dieser Speicherreihenfolge führt den Freigabevorgang aus: Nach diesem Speicher können keine Speicherzugriffe im aktuellen Thread neu angeordnet werden. Dies stellt sicher, dass alle Schreibvorgänge in dem aktuellen Thread in anderen Threads sichtbar sind, die die gleiche atomare Variable erhalten, und Schreibvorgänge, die eine Abhängigkeit in die atomare Variable tragen, werden in anderen Threads sichtbar, die denselben atomaren Datentyp verwenden.

So bedeutet das, dass jeder Thread, der die Änderung ready sieht, wird es keine schreibt, bevor es getan sehen, welche die, die zu buff enthält. Daher ist dieser Teil Ihres Codes in Ordnung, soweit Datenrennen gehen. (Es ist schrecklich für viele andere Gründe, natürlich.)

+0

danke, können Sie mich einige zeigen dieser Gründe? – enger

+1

Das offensichtlichste ist das 'while (! Ready);', das um Ressourcen mit dem wartenden Thread konkurrieren kann. Stellen Sie sich zum Beispiel vor, wenn die beiden Threads in einer hyper-threaded CPU in demselben physischen Kern ausgeführt werden. Was passiert auch, wenn diese "while" -Schleife endlich ausbricht? Sie werden die Mutter aller falsch vorhergesagten Zweige nehmen. Natürlich codierst du in einer höheren Sprache, also solltest du dir nicht wirklich Sorgen machen, wie oder warum es schlecht ist. Denken Sie nur daran, dass die Sprache Möglichkeiten hat, solche Dinge richtig zu machen, und das sind sie nicht *.Atomics sind nicht zum Warten da. –

+0

danke, aber welche andere Lösung für den wartenden Thread kann ich tun, wenn ich detach(); vor der Weile (! bereit); ? Es tut mir leid, ich bin nur neu in Multi-Threading in C++ 11. – enger

0

Basierend auf den Empfehlungen von David Schwartz und Igor Tandetnik, ich den Code geändert Bedingungsvariablen zu verwenden, und den Faden zu lösen:

#include <iostream> 
#include <thread> 
#include <chrono> 
#include <condition_variable> 

int main(){ 
    std::mutex mutex_condition; 
    std::condition_variable condition; 
    bool ready= false; 
    char buff[20]= "hello"; 

    std::cout << "creating thread to change \"buff\"..." << std::endl; 
    std::thread tr(
     [ & condition, & mutex_condition, & ready, & buff ](){ 
      { 
       std::lock_guard<std::mutex> lock_(mutex_condition); 
       std::this_thread::sleep_for(std::chrono::milliseconds(100)); 
       buff[0]= 'w'; buff[1]= 'o'; 
       buff[2]= 'r'; buff[3]= 'l'; 
       buff[4]= 'd'; buff[5]= '\0'; 
       std::cout << "buffer changed! sending signal to waiting thread..." << std::endl; 
       ready= true; 
      } 
      condition.notify_one(); 
    }); 

    std::cout << "...calling detach..." << std::endl; 
    tr.detach(); 

    std::cout << "...waiting for the change..." << std::endl; 
    std::unique_lock<std::mutex> lock_(mutex_condition); 
    condition.wait(lock_, [ & ready ]{ return ready; }); 

    std::cout << "...got it! buff=" << buff << std::endl; 
} 

Thank you! Hoffentlich könnte das jemand anderem helfen.