Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

MultiThreading - Änderung einer Variable wird im Thread nicht erkannt
Lost-Ha(n)f-PHP
myCSharp.de - Member

Avatar #avatar-2265.jpg


Dabei seit:
Beiträge: 58

Themenstarter:

MultiThreading - Änderung einer Variable wird im Thread nicht erkannt

beantworten | zitieren | melden

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
private Nachricht | Beiträge des Benutzers
zommi
myCSharp.de - Member

Avatar #avatar-2617.png


Dabei seit:
Beiträge: 1.361
Herkunft: Berlin

beantworten | zitieren | melden

volatile sollte helfen.

beste Grüße
zommi
private Nachricht | Beiträge des Benutzers
dN!3L
myCSharp.de - Experte

Avatar #avatar-2985.png


Dabei seit:
Beiträge: 2.891

beantworten | zitieren | melden

Zitat von Lost-Ha(n)f-PHP
[...]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
private Nachricht | Beiträge des Benutzers
Corpsegrinder
myCSharp.de - Member



Dabei seit:
Beiträge: 401

beantworten | zitieren | melden

Locks sollten aber beim Zugriff von mehreren Threads auf mutable Felder immer gesetzt werden, da es sonst zu Race-Conditions kommt.
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 49.486
Herkunft: Berlin

beantworten | zitieren | melden

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
private Nachricht | Beiträge des Benutzers
chilic
myCSharp.de - Experte



Dabei seit:
Beiträge: 2.099

beantworten | zitieren | melden

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?
private Nachricht | Beiträge des Benutzers
Corpsegrinder
myCSharp.de - Member



Dabei seit:
Beiträge: 401

beantworten | zitieren | melden

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
private Nachricht | Beiträge des Benutzers
ujr
myCSharp.de - Experte



Dabei seit:
Beiträge: 1.688

beantworten | zitieren | melden

Zitat von chilic
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.
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 49.486
Herkunft: Berlin

beantworten | zitieren | melden

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
Zitat
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
private Nachricht | Beiträge des Benutzers
zommi
myCSharp.de - Member

Avatar #avatar-2617.png


Dabei seit:
Beiträge: 1.361
Herkunft: Berlin

beantworten | zitieren | melden

Zitat von ujr
"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.
private Nachricht | Beiträge des Benutzers
Lost-Ha(n)f-PHP
myCSharp.de - Member

Avatar #avatar-2265.jpg


Dabei seit:
Beiträge: 58

Themenstarter:

beantworten | zitieren | melden

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.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Lost-Ha(n)f-PHP am .
Mit freundlichem Gruß

Lost-Ha[n]f-PHP
private Nachricht | Beiträge des Benutzers
Corpsegrinder
myCSharp.de - Member



Dabei seit:
Beiträge: 401

beantworten | zitieren | melden

@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.
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 49.486
Herkunft: Berlin

beantworten | zitieren | melden

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
private Nachricht | Beiträge des Benutzers