threads und externe variablen
-
Guten tag,
Ich habe im moment ein kleines problem und wüsste gerne wie ich
das fixxen kann. Nunja, in einer CPP datei habe ich einen
Thread und an einer gewissen stelle ist eine Whileschleife.
Diese whileschleife überprüft ob ein DWORD 0 ist.
Diese DWORD variable, wird in dieser CPP datei definiert,
und in einer anderen mit extern deklariert. So das problem ist jetzt,
das diese variable in der CPP datei verändert wird in der sie
als extern definiert ist, und der Thread bleibt in der Whileschleife
hängen. Kann man externe variablen nicht verändern ?
Wenn nicht, bzw wenn der Thread es nicht mitbekommt, wäre ich sehr offen
für Vorschläge wie ich dieses problem lösen könnte.
Vielen dank im vorraus für eure HilfeGruß
beginner31231
-
Du kannst die Variable schon verändern, aber dein Thread bekommt davon u.U. nichts mit, da der Compiler z.B. beim Optimieren veranlasst haben kann, dass der Inhalt der Variable in ein Register geladen wird. Dass die Variable woanders verändert wird, ahnt er nicht. Das kannst du ihm aber mitteilen, indem du sie als volatile deklarierst. Dann darf der Compiler den Inhalt der Variablen nicht zwischenspeichern oder sonstige Annahmen über deren Inhalt machen.
-
was soll das hier überhaupt bringen die Variable als extern zu deklarieren?
-
beginner31231 schrieb:
Nunja, in einer CPP datei habe ich einen
Thread und an einer gewissen stelle ist eine Whileschleife.
Diese whileschleife überprüft ob ein DWORD 0 ist.
Diese DWORD variable, wird in dieser CPP datei definiert,Wie sieht diese Definition aus?
beginner31231 schrieb:
und in einer anderen mit extern deklariert. So das problem ist jetzt,
das diese variable in der CPP datei verändert wird in der sie
als extern definiert ist,definiert oder deklariert?
beginner31231 schrieb:
und der Thread bleibt in der Whileschleife
hängen. Kann man externe variablen nicht verändern ?Klar, kann man das -- sofern sie nicht const ist.
Ich kann mir mehrere Gründe vorstellen.
1. Speicherzugriff wurde wegoptimiert
2. Du hast in Wirklichkeit 2 versch. VariablenZu 1 musst Du wissen, dass der Aufbau einer CPU recht kompliziert ist und Teile des Hauptspeichers in CPU-eigenen Zwischenspeicher ("Cache") vorrätig gehalten wird. Wenn also CPU A in den Speicher schreibt und CPU B die Stelle lesen will, aber noch auf den Cache zugreift, "sieht" CPU B diesen Schreibvorgang nicht. Selbst auf Compiler-ebene könnte der Speicherzugriff wegoptimiert worden sein.
Wie es richtig geht: Zugriff auf gemeinsamen Speicher synchronisieren. Unsynchronisierter Zugriff nennt sich auch "Datenrennen". Zur Synchronisierung verwendest Du zB einen "kritischen Abschnitt". Das sollte Teil der Threading-Bibliothek sein, die Du verwendest. Nebenläufige Threads starten ist nicht das einzige, was diese Bibliotheken leisten. Sie bieten auch Mittel zur Synchronisierung und Koordination an.
Tipp: Versuche die gemeinsame Nutzung derselben Variablen zwischen versch Threads zu vermeiden. Das gibt leider sehr schnell Chaos und man kann Programmierfehler sehr schlecht nachvollziehen.
Gruß,
SP
-
????????????????????????? schrieb:
was soll das hier überhaupt bringen die Variable als extern zu deklarieren?
Bist Du Dir dessen Effekt nicht bewusst oder ist das eine Design-Frage, warum man so etwas jemals machen sollte?
extern erzwingt externe Bindung und kann aus einer Definition eine Deklaration machen:
extern int i; // de[b]klar[/b]iert i (externe Bindung) int i; // de[b]fin[/b]iert i (externe Bindung) const int j = 23; // de[b]fin[/b]iert j (interne Bindung, da const) (*) extern const int k; // de[b]klar[/b]iert k (externe Bindung) extern const int k = 42; // de[b]fin[/b]iert k (externe Bindung)(*) gilt meines Wissens nach nur für C++, nicht für C. In C müsste man hier noch das Schlüsselwort
staticeinsetzen, um interne Bindung zu erzwingen.Gruß,
SP
-
Danke für die ganzen Antworten.
Habe mich ein wenig eingelesen und benutze eine critical section.
Habe auch volatile probiert aber beides kam zu keiner lösung.
Momentan hab ichs jetzt anders, ich habe eine Funktion
die einen vector prüft ob ein object mit einem
bestimmt wert vorhanden ist oder nicht, und gibt dann halt
true oder false zurück. Das heisst ich hab jetzt eine whileschleife
im Thread die bedingung ist das ein bool true ist.
Soo in der Whileschleife wird jetzt mit einer function
gecheckt ob in einem vector etwas vorhanden ist,
und wenn ja gibt diese funktion true zurück, wenn nicht dann false.
Diese funktion setzt auch die bool variable die die Bedingung ist.
Nun ist es leider so, das der vector innerhalb des Threads anscheinend
nicht geupdatet wird. Der Vector selber bekommt nebenbei neue werte,
löscht werte etc in dem mainprogramm. Damit es da keine Fehler gibt,
benutze ich wie vorher schon gesagt eine critical section und
EnterCriticalSection bzw LeaveCriticalSection. Kann es da trotzdem
noch probleme geben ? wenn ja welche ?
Weil naja, es klappt eigentlich das nebenbei sachen eingefügt bzw gelöscht
werden nur innerhalb des threads scheint die funktion das nicht mizukriegen.
-
Du hast irgendwo einen Fehler gemacht, aber wo, können wir auch nicht sagen ohne den Code zu sehen. Wenn Zugriffe auf gemeinsam genutzte Objekte mit critical sections synchronisiert werden, dann reicht das.
Du musst dich halt mit volatile dagegen absichern, dass der Compiler gut gemeinte Optimierungen anbringt.
Betrachte folgendes Beispiel:static bool quit; void thread() { quit=false; while (!quit) { //mach was } }Der Compiler kann sich jetzt while (true) denken, da sich der Wert von quit unter normalen Bedingungen nicht ändern kann. Wenn aber "abnormale" Bedingungen vorliegen, sprich, dass quit in einem anderen Thread verändert wird, musst du dich gegen solche Optimierungen mit volatile static bool quit absichern.
-
Sebastian Pizer schrieb:
????????????????????????? schrieb:
was soll das hier überhaupt bringen die Variable als extern zu deklarieren?
oder ist das eine Design-Frage, warum man so etwas jemals machen sollte?
Ja, hab extern noch nie gebraucht. Gibts da auch sinnvolle Beispiele, für Threads brauch ich es nicht.
-
beginner31231 schrieb:
Habe mich ein wenig eingelesen und benutze eine critical section.
Habe auch volatile probiert aber beides kam zu keiner lösung.volatile ist Blödsinn hier. Ein(e) Mutex/CriticalSection ist vernünftig und funktioniert auch bei richtiger Anwendung.
beginner31231 schrieb:
Kann es da trotzdem noch probleme geben ? wenn ja welche ? Weil naja, es klappt eigentlich das nebenbei sachen eingefügt bzw gelöscht werden nur innerhalb des threads scheint die funktion das nicht mizukriegen.
Dann hast Du wohl etwas falsch gemacht.
Ich denke, Du solltest Dich "noch mehr einlesen" in die Materie.
Gruß,
SP
-
-
Athar schrieb:
Sebastian Pizer schrieb:
volatile ist Blödsinn hier.
Wie kommst du darauf?
siehe Link!
-
Sebastian Pizer schrieb:
siehe Link!
... und was willst du damit sagen? Da wird der Sinn von volatile beschrieben.
Wenn er auf die Veränderungen irgendwelcher Flags von außen wartet, muss er es hier höchstwahrscheinlich einsetzen. Um darüber eine Aussage treffen zu können, müsste man aber erstmal Code sehen.
-
Athar schrieb:
...irgendwelcher Flags von außen wartet, muss er es hier höchstwahrscheinlich einsetzen.
Was für "Flags von außen"? Ich gehe nicht davon aus, dass der OP Gerätetreiber programmieren will.
Entweder wird der Zugriff auf ein gemeinsames "Objekt" über einen Mutex synchronisiert (ist am einfachsten und weniger fehleranfällig), oder mit so etwas wie spinlocks + memory barrier -- lezteres erfordert aber atomare lese/schreib/austausch-Operationen, die
volatile(aus C und C++) nicht garantiert. Außerdem ist das eine ziemliche low-level Frickelei, welche man nur Experten überlassen sollte. Ich zähle mich übrigens nicht zu dieser Experten-Gruppe.Gruß,
SP
-
Naja das was ich mache ist, das vor der ersten benutzung einmal
InitializeCriticalSection aufgerufen wird, und dann vor jedem
zugriff auf den Vector EnterCriticalSection, und danach LeaveCriticalSection
calle. Hab ich da irgendwas verpasst ? :>
Mit volatile werd ichs nochmal probieren ^^
-
edit:
Das Problem ist nicht die Bool Variable.bool funktion(int id) { if(!initalized) { InitializeCriticalSection(&crit); initalized = true; } vector<example>::iterator it; EnterCriticalSection(&crit); it = structvector.begin(); while(it != structvector.end()) { if (it->id == id) { LeaveCriticalSection(&crit); return true; } else it++; } LeaveCriticalSection(&crit); return false; }So in etwa sieht die checkfunktion aus. Mach ich da irgendwas falsch ?
Da sieht man auch direkt wie ich die Critical Section verwende halt vor jedem
zugriff auf den Vector.
Die Schleife die das testet hat auch einen Sleep und testet nicht durchgehend.
ca so:while(boolvar) { Sleep(2000); boolvar = function(struct->id); }Wenn daran irgendwas falsch ist, wäre es nett mehr zu sagen
was ich falsch mache und wieso das falsch ist. Vielen dank
das ihr euch solche Mühe gebt
-
beginner31231 schrieb:
Mach ich da irgendwas falsch ?
Ja, denn die Initialisierung von crit erzeugt eine race condition, z.B.
// Thread 1 | Thread 2 if(!initalized) | if(!initalized) { | { InitializeCriticalSection(&crit); | initalized = true; | } | vector<example>::iterator it; | EnterCriticalSection(&crit); | | InitializeCriticalSection(&crit); | initalized = true; | } | vector<example>::iterator it; | EnterCriticalSection(&crit); // upsStatements, die in der gleichen Zeile stehen, stelle man sich als gleichzeitig ausgeführt vor.
Das Synchronisationsobjekt muss natürlich initialisiert worden sein, bevor mehrere Threads versuchen können, dieses zur Synchronisation zu verwenden. Zweckmäßigerweise macht man das also gleich bei der Erzeugung des zu schützenden vector.
Die Benutzung von RAII ist zu empfehlen.
-
Danke für die Informationen. Nunja, zu der Sache gleich bei der Erzeugung.
Ich habe den Vector nicht in dem mainprogramm sondern in einer anderen
cpp datei. Das heißt ich müsste den vector schon im main Programm erzeugen
und dann in der anderen cpp als extern angeben ?
Sprich ich müsste das im main Programm ca so machen -->CRITICAL_SECTION crit; InitializeCriticalSection(&crit); vector<example>vectorname;und in der anderen cpp dann halt
extern vector<example>vectorname;und wie vorher dann enter bzw leave benutzen nur
das initialize natürlich rausnehmen dann ?
Bei der erzeugung müsste ich jetzt nicht EnterCriticalSection benutzen ?!
Vielen dank nochmal für die Hilfe.
-
beginner31231 schrieb:
Danke für die Informationen. Nunja, zu der Sache gleich bei der Erzeugung.
Ich habe den Vector nicht in dem mainprogramm sondern in einer anderen
cpp datei. Das heißt ich müsste den vector schon im main Programm erzeugen
und dann in der anderen cpp als extern angeben ?
Sprich ich müsste das im main Programm ca so machen -->CRITICAL_SECTION crit; InitializeCriticalSection(&crit); vector<example>vectorname;und in der anderen cpp dann halt
extern vector<example>vectorname;und wie vorher dann enter bzw leave benutzen nur
das initialize natürlich rausnehmen dann ?
Bei der erzeugung müsste ich jetzt nicht EnterCriticalSection benutzen ?!
Vielen dank nochmal für die Hilfe.Das ist korrekt, mein Herr!