Frage

Ich habe eine Anwendung, bei der zwei Threads laufen ... Gibt es eine certanty, dass wenn ich eine globale Variable von einem Thread ändern, wird der andere diese Veränderung bemerkt? Ich habe keine syncronization oder Ausschlußart System vorhanden ... aber sollte dieser Code Arbeit die ganze Zeit (eine globale Bool Namen dataUpdated vorstellen):

Gewinde 1:

while(1) {
    if (dataUpdated)
        updateScreen();
    doSomethingElse();
}

Thread 2:

while(1) {
    if (doSomething())
        dataUpdated = TRUE;
}

Hat ein Compiler wie gcc optimieren diesen Code in einer Weise, dass es nicht für den globalen Wert überprüft, nur es Wert zum Zeitpunkt der Kompilierung unter Berücksichtigung (weil es in der gleichen thred geändert NEVERS bekommen)?

PS: Da dies für eine Spiel-ähnliche Anwendung, es doe Sache wirklich, wenn es eine Lese sein, während der Wert geschrieben wird ... alles, was zählt, ist, dass die Änderung von dem anderen Thread bemerkt wird <. / p>

War es hilfreich?

Lösung

Ja. Nein. Vielleicht.

Zuerst wird, wie andere erwähnt haben Sie brauchen dataUpdated flüchtig zu machen; sonst der Compiler frei heben kann es aus der Schleife zu lesen (abhängig davon, ob oder nicht, kann es sehen, dass doSomethingElse es nicht berühren).

Zweitens je nach Prozessor und Bestellanforderungen, können Sie Speicherbarrieren müssen. flüchtig genug ist, dass der andere Prozessor schließlich die Änderung zu garantieren, aber nicht genug, um zu garantieren, dass die Änderungen in der Reihenfolge gesehen werden sehen, sie wurden durchgeführt. Ihr Beispiel hat nur eine Flagge, so dass es nicht wirklich dieses Phänomen zeigen. Wenn Sie benötigen und Speicherbarrieren verwenden, sollten Sie nicht mehr flüchtig benötigen

Volatile als schädlich und Linux Kernel-Speicher Barrieren guten Hintergrund auf den zugrunde liegenden Probleme sind; Ich weiß speziell für Threading nicht wirklich von etwas ähnliches geschrieben. Zum Glück Fäden nicht erhöhen diese Bedenken fast so oft wie Hardware-Peripherie tun, obwohl die Art von Fall, dass Sie beschreiben (ein Flag Abschluss angibt, mit anderen Daten gültig sein vermuten, wenn das Flag gesetzt ist) ist genau die Art von Sache, wo die Bestellung matterns ...

Andere Tipps

Hier ist ein Beispiel, die Boost-Zustandsgrößen verwendet:

bool _updated=false;
boost::mutex _access;
boost::condition _condition;

bool updated()
{
  return _updated;
}

void thread1()
{
  boost::mutex::scoped_lock lock(_access);
  while (true)
  {
    boost::xtime xt;
    boost::xtime_get(&xt, boost::TIME_UTC);
    // note that the second parameter to timed_wait is a predicate function that is called - not the address of a variable to check
    if (_condition.timed_wait(lock, &updated, xt))
      updateScreen();
    doSomethingElse();
  }
}

void thread2()
{
  while(true)
  {
    if (doSomething())
      _updated=true;
  }
}

Verwenden Sie eine Sperre. Verwenden Sie immer immer eine Sperre auf gemeinsame Daten zugreifen. die Variable als flüchtige Markierung wird der Compiler die Optimierung entfernt die Speicherlese verhindern, aber nicht andere Probleme vermeiden, wie zum Beispiel Speicher Nachbestellung . Ohne eine Sperre gibt es keine Garantie, dass der Speicher in doSomething schreibt () in der updateScreen () Funktion sichtbar.

Die einzige andere sichere Art und Weise ist ein Speicher Zaun zu verwenden, entweder explizit oder implizit eine Verwendung eine verriegelte * Funktion zum Beispiel.

Mit der volatile Schlüsselwort für den Compiler anzudeuten, dass der Wert jederzeit ändern können.

volatile int myInteger;

Die oben garantiert, dass jeder Zugriff auf die Variable und aus dem Speicher wird ohne spezifische Optimierungen und als Ergebnis alle Threads auf dem gleichen Prozessor ausgeführt werden Änderungen an die Variablen mit der gleichen Semantik „sehen“, wie der Code liest .

Chris Jester-Young wies darauf hin, dass die Kohärenz Bedenken auf einen solchen variablen Wertänderung in einem Multi-Prozessor-Systemen auftreten können. Dies ist eine Überlegung, und es hängt von der Plattform.

Tatsächlich gibt es wirklich zwei Überlegungen in Bezug auf Plattform zu denken. Sie sind Kohärenz und Unteilbarkeit der Speichertransaktionen.

Atomicity ist eigentlich eine Gegenleistung für Einzel- und Multi-Prozessor-Plattformen. Das Problem entsteht, weil die Variable wahrscheinlich Multi-Byte in der Natur ist und die Frage ist, ob ein Thread eine Teilaktualisierung auf den Wert sehen konnte oder nicht. dh: Einige Bytes geändert, Kontextwechsel, ungültigen Wert lesen by thread zu unterbrechen. Für eine einzelne Variable, die bei der natürlichen Maschinenwortgröße oder kleiner und natürlich ausgerichtet sollte kein Problem sein soll. Insbesondere wird ein int Typ immer OK in dieser Hinsicht sein sollte, solange es ausgerichtet ist -. Was der Standardfall für den Compiler sein sollte

Im Vergleich zu Kohärenz, ist dies ein potenzielles Problem in einem Mehrprozessorsystem. Die Frage ist, ob das System vollständig Cache-Kohärenz oder nicht zwischen den Prozessoren implementiert. Wenn implementiert, wird dies in der Regel mit dem MESI-Protokoll in Hardware durchgeführt. Die Frage keine staatliche Plattformen, aber beide Intel x86-Plattformen und PowerPC-Plattformen sind cachekohärenten über Prozessoren für normalerweise Programmdatenbereichen zugeordnet. Deshalb ist diese Art von Problem sollte kein Problem für die normalen Datenspeicher sein, greift zwischen den Threads, auch wenn es mehrere Prozessoren.

Die letzte Ausgabe in Bezug auf die Unteilbarkeit, die spezifisch entsteht auf Read-Modify-Write-Atomarität. Das heißt, wie Sie garantieren, dass, wenn ein Wert gelesen wird, in Wert aktualisiert und die geschrieben, dass diese atomar passieren, auch über Prozessoren, wenn mehr als eine. Also, für diese ohne spezielle Synchronisationsobjekte zu arbeiten, würde erfordern, dass alle potenziellen Threads Zugriff auf die Variable sind Leser nur, aber nur für einen Thread erwarten kann je ein Schriftsteller auf einmal sein. Ist dies nicht der Fall ist, dann müssen Sie ein Sync-Objekt zur Verfügung zu können atomare Aktionen auf RMW-Befehl Aktionen auf die Variable gewährleisten.

Ihre Lösung wird zu 100% CPU verwenden, unter anderem Probleme. Google für "Bedingungsvariable".

Chris Jester-Young wies darauf hin, dass:

  

Dies ist nur Arbeit unter Java 1.5 + 's-Speicher-Modell. Die C ++ Standard befasst sich nicht mit Gewinde und flüchtige garantiert keine Speicherkohärenz zwischen den Prozessoren. Sie haben eine Speicherbarriere für diese benötigen

so ist, die einzig wahre Antwort ist ein Synchronisationssystem implementiert, nicht wahr?

Mit dem volatilen Schlüsselwort für die Compiler anzuzudeuten, dass der Wert jederzeit ändern kann.

volatile int myInteger;

Nein, es ist nicht sicher. Wenn Sie die Variable volatile deklarieren, als der Compiler sollten Code generieren, die immer die Variable auf einer Lese aus dem Speicher geladen wird.

Wenn der Bereich rechts ( „extern“, global, etc.), dann wird die Änderung bemerkt werden. Die Frage ist, wann? Und in welcher Reihenfolge?

Das Problem ist, dass der Compiler kann und häufig re-order Ihre Logik alle zu füllen, es ist gleichzeitig Pipelines als Performance-Optimierung.

Es zeigt nicht wirklich in Ihrem speziellen Beispiel, weil es um Ihre Zuordnung keine andere Anweisungen sind, aber vorstellen, Funktionen erklärt, nachdem Ihre Bool assign ausführen vor die Zuordnung.

Check-out Pipeline Hazard Wikipedia oder Google-Suche nach „Compiler Anweisung Neuordnungs „

Wie schon andere gesagt haben, das volatile Schlüsselwort ist dein Freund. : -)

Sie werden wahrscheinlich feststellen, dass Ihr Code würde funktionieren, wenn Sie in gcc deaktiviert alle Optimierungsmöglichkeiten haben. In diesem Fall (glaube ich) es alles so flüchtig behandelt und als Ergebnis wird die Variable im Speicher für jede Operation abgerufen.

Mit jeder Art von Optimierung auf dem Compiler gedreht wird versuchen, eine lokale Kopie in einem Register gehalten zu verwenden. Je nach Ihren Funktionen kann dies bedeuten, dass Sie nur die Änderung der Variable sehen intermittierend oder, im schlimmsten Fall, niemals.

das Stichwort volatile Mit zeigt den Compiler mit, dass der Inhalt dieser Variablen jederzeit ändern kann, und dass es sollte nicht verwenden, um eine lokal gespeicherte Kopie.

Mit all das gesagt, Sie bessere Ergebnisse finden kann (wie angedeutet durch Jeff ) durch die Verwendung eines Semaphore oder Bedingungsvariable.

Diese ist eine angemessene Einführung in das Thema.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top