Laden...

MultiThreading - Änderung einer Variable wird im Thread nicht erkannt

Erstellt von Lost-Ha(n)f-PHP vor 13 Jahren Letzter Beitrag vor 13 Jahren 2.427 Views
Lost-Ha(n)f-PHP Themenstarter:in
58 Beiträge seit 2007
vor 13 Jahren
MultiThreading - Änderung einer Variable wird im Thread nicht erkannt

Hallo Community,

ich arbeite gerade an einer Anwendung, die mehrere Threads benutzt.
Der Main.Thread startet zwei weitere Threads. In denen wir eine Schleife durchlaufen, bis die _Connected (bool) false gesetzt wird.

_Kleine Information:
Die deinen weiteren Threads sind private Member einer Klasse aus der auch die beiden Threads vom Main-Thread gestartet werden. Auch Connected ist ein privater Member dieser Klasse. Alle Zugriffe spielen sich auch innerhalb diese Klasse ab.

Das Problem an der Geschichte ist, dass der eine Thread diese Umstellung von true auf false nie, der andere manchmal mitbekommt. D.h. die Thread laufen bin in die Ewigkeit weiter und die Anwendung im Hintergrund ebenfalls...

Woran liegt das? Ist das Problem sicher zu umgehen, wenn ich ein LockObject anlege und jejden Zugriff über lock regele? Oder gibt kann es da noch ein anderes Problem geben?

Ich werde mich mal daran setzen und das Lock Implementieren, aber das ist wohl großer Aufwand, weshalb ich gerne schon wüsste, ob sich das alle überhaupt loht / lohnen kann.

Ich bin für jegliche Hilfe dankbar.

Mit freundlichem Gruß

Lost-Ha[n]f-PHP

1.361 Beiträge seit 2007
vor 13 Jahren

volatile sollte helfen.

beste Grüße
zommi

2.891 Beiträge seit 2004
vor 13 Jahren

[...]das Lock Implementieren, aber das ist wohl großer Aufwand

Warum? Einfach alle Variablenzugriffe mit der lock-Anweisung nachrüsten.
Aber guck dir mal das volatile-Schlüsselwort an.

Gruß,
dN!3L

C
401 Beiträge seit 2007
vor 13 Jahren

Locks sollten aber beim Zugriff von mehreren Threads auf mutable Felder immer gesetzt werden, da es sonst zu Race-Conditions kommt.

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo Corpsegrinder,

wenn die Variablen atomar les- und schreibbar sind, wie das bei einer boolschen Variable der Fall ist, ist lock oft nicht erforderlich. In den Fällen, wo man kein lock braucht, kommt es dann trotzdem nicht zu Race-Conditions. Für den hier diskutierten Fall einer Abbruchvariable braucht man jedenfalls kein lock.

herbivore

C
2.121 Beiträge seit 2010
vor 13 Jahren

Für mich klingt es auch nach einem anderen Problem als lock. Ist sichergestellt dass die Threads wirklich auf die selbe Variable zugreifen, oder gibts da vielleicht mehrere Klassen mit der Variable und die Threads greifen auf die falsche zu?

C
401 Beiträge seit 2007
vor 13 Jahren

Hallo herbivore,

das stimmt natürlich. Das Ganze ändert sich aber, wenn man mit Properties arbeitet. Ich muss wohl beim ersten Durchlesen übersehen haben, dass der Threadersteller bereits erwähnte, dass es sich um Felder handelt. Jedoch finde ich es wichtig zu erwähnen, das auch bei primitiven Typen eben in manchen Fällen auch ein volatile nicht reicht. Hierzu noch ein Beispielcode:


namespace ConsoleApplication1 {
  using System;
  using System.Threading;

  internal class Program {
    private static void Main(string[] args) {
      var test = new TestFoo();
      var rnd = new Random(DateTime.Now.Millisecond);
      var t1 = new Thread(() => {
                            while (true) {
                              test.Foo = Convert.ToBoolean(rnd.Next(2));
                              Console.WriteLine("Thread 1: {0}", test.Foo);
                            }
                          });
      var t2 = new Thread(() => {
                            while (true) {
                              test.Foo = Convert.ToBoolean(rnd.Next(2));
                              Console.WriteLine("Thread 2: {0}", test.Foo);
                            }
                          });
      t1.Start();
      t2.Start();
      t1.Join();
      t2.Join();
    }

    #region Nested type: TestFoo

    private class TestFoo {
      private volatile bool foo;

      public bool Foo {
        get { return foo; }
        set {
          Console.WriteLine("Setting foo to {0}.", value);
          foo = value;
        }
      }
    }

    #endregion
  }
}

Früher oder später wird hier folgende Zeile erscheinen:

Setting foo to true.
Thread 1: true
Thread 2: false

U
1.688 Beiträge seit 2007
vor 13 Jahren

Für mich klingt es auch nach einem anderen Problem als lock.

Das sowieso (s.o.). Aber auch "volatile" sollte nicht die Lösung sein (sofern es sich um eine x86/x64 Architektur handelt). "volatile" an sich ist natürlich sachlich richtig, aber eben auf x86 nicht unbedingt notwendig.

Also könnte eher eine der von chilic vermuteten Ursachen verantwortlich sein. Leider gibt's keine Code-Schnippsel zu sehen.

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo Corpsegrinder,

dein Beispiel ist doch kein Beweis. Nehmen wir an foo ist false. Jetzt ruft Thread 1 den Setter von Foo mit true auf. Das bewirkt die Ausgabe von: "Setting foo to True." Nehmen wir weiter an, direkt nach der Ausgabe und vor dem Setzen von foo erfolgt ein Threadwechsel. Dann kommt Thread 2 an die Reihe. Er fragt Foo und damit foo ab. Der Wert ist natürlich immer noch false. Nehmen wir an, jetzt erfolgt wieder ein Threadwechsel. Jetzt kommt Thread 1 dran. Er führt jetzt erst die Zeile "foo = value;" aus. Erst jetzt ist foo true. Anschließend gibt er das aus.

Die Ausgabe von

Setting foo to true.
Thread 1: true
Thread 2: false

ist also kein Beweis dafür, dass durch die Verwendung von Properties echt was schiefgeht. Es zeigt einfach nur, dass Consolen-Ausgaben kein adäquates Mittel sind, um die tatsächlichen Abläufe exakt darzustellen.

herbivore

1.361 Beiträge seit 2007
vor 13 Jahren

"volatile" an sich ist natürlich sachlich richtig, aber eben auf x86 nicht unbedingt notwendig.

Volatile soll ja im besten Fall mehrere Dinge erzwingen:*Compiler läd den Wert auf keinen Fall aus nem Register, immer vom Speicher. *Erzwingen von Cache-Coherence bei Multi-Core/Cache-Systemen *Kein Umordnen der CPU-Instruktionen (seitens Compiler + CPU) *Feste Reihenfolge, wann Werte im Speicher erscheinen und geholt werden (Memory-Barrier mit Read/Write-Semantik) *...

Bereits der erste Punkt ist essentiell und verlangt eine Angabe von volatile. Und dies ist unabhängig von der Architektur. Dass auf ner x86-Architektur manche der anderen Punkte immer erfüllt sind, ist eine andere Sache.

Lost-Ha(n)f-PHP Themenstarter:in
58 Beiträge seit 2007
vor 13 Jahren

Hallo Leute,
danke für eure zahlreichen Antworten.
volatile hat das Problem anscheinend gelöst, da es bisher kein einziges Mal mehr dazu gekommen war.

Mit freundlichem Gruß

Lost-Ha[n]f-PHP

C
401 Beiträge seit 2007
vor 13 Jahren

@herbivore

Naja... mal angenommen wir haben eine Queue und die Queue hat eine Maximale Größe und mal angenommen wir haben einen Wert, der auf true gesetzt wird, wenn die Queue voll ist. In diesem Fall würde es hier schief gehen, weil der Thread 1 die Queue voll macht, aber bevor er die Variable setzen kann fragt Thread 2, ob die Queue voll ist und bekommt ein false. Oder meinetwegen bekommen die Threads Indizes. Genau das Gleiche. Vielleicht sind Konsolenausgaben nicht der richtige Weg um das zu zeigen, aber diese Threadwechsel sind doch das Problem.

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo Corpsegrinder,

ich verstehe nicht, worauf du hinaus willst. In deinem neuen Fall erfolgt die Änderung nicht atomar sondern es sind eben zwei Zugriffe erforderlich (das Hinzufügen des Elements und das Setzen der boolschen Variable), um eine konsistente Zustandsänderung herbeizuführen. Sowas muss man bei mehreren Threads natürlich synchronisieren. Davon abgesehen habe ich selbst bei atomaren Zugriffen nie behautet, dass eine Synchronisation stets überflüssig ist. Es kommt immer auf den Fall an. Im konkreten Fall des OPs ist eine Synchronisation jedoch überflüssig.

herbivore