Hallo. Ich habe ein kleines Problem mit Threads unter Windows. Ich lese
zunächst die Anzahl der Cores aus (GetSystemInfo) und erstelle dann pro
Core einen Thread. Diese Threads sollen dann zeitgleich gestartet werden
und immer eine Sekunde lang Berechnungen durchführen. Jede Sekunden wird
dann durch ein Timer-Event ein Label aktualisiert, wo die Summe der
Schleifendurchläufe Aller Threads drin stehen soll.
Das Problem ist, dass ich nicht weiß wie ich dem Timer-Event mitteilen
kann, dass ALLE Threads ihre erste Berechnung abgeschlossen haben, und
den Threads selber dann Mitteilen kann, dass sie nun weiterrechnen
können. Ich habe exemplarisch einfach mal die "bench_valid"-Variable
dahin gemacht damit man sieht wie ich das meine.
Ich könnte jetzt natürlich ein Array machen wo jeder Thread dann seine
Variable auf "1" setzt, das ließe sich aber schlecht im Timer-Event
abfragen. Oder ich nehme eine short-Variable wobei jeder Thread ein Bit
setzen kann. Dann müsste ich jedoch mit Mutexen arbeiten, und würde dann
im Timer-Event auf "0xF" prüfen. Aber vielleicht gehts ja auch viel
einfacher. Bin für jeden Tipp dankbar.
Momentan siehts in etwa so aus (stark gekürzt):
Nachdem eh jeder Thread seinen Index hat, kannst du doch noch ein
Feld mit entsprechend vielen Statusbytes definieren.
Zu Anfang werden alle auf 0 gesetzt, jeder Thread setzt seinen
Eintrag wenn er fertig ist z.B. auf 1.
Daran sieht das Hauptprogramm, daß der jeweilige Thread durch ist.
Wenn es ein Eintrag ist, der atomar gesetzt/gelesen werden kann
(das wäre es bei char oder auf PCs auch bei short und int),
braucht man dafür keinen Mutex.
Wenn sie weiter rechnen sollen, halt wieder auf 0 setzen oder Event
schicken.
Alternativ: jeder Thread löst am Ende ein Event aus.
Und volatile nicht vergessen für globale Variablen...
PS: Tippfehler korrigiert...
Wenn ich jetzt ein Feld mache dann müsste ich ja im Timer-Event im
schlechtesten fall 16 Variablen überprüfen, das erscheint mir etwas zu
umständlich. Oder wie meintest du das?
ja, so meinte ich das.
(Eine andere Möglichkeit wäre, nur eine Variable zu nehmen, die
von jedem Thread um eins hochgezählt wird. Das erfordert dann
aber wieder einen Mutex und ist unterm Strich ineffizienter.)
Das Hauptprogramm braucht aber doch gar nicht bei jedem Test
durch alle Feldelemente laufen; es testet den ersten und bleibt
stehen, wenn der noch nicht da ist.
Erst wenn der erste Thread sein Ende signalisiert, wird der
zweite getestet und ggf. wieder stehen geblieben usw.; bis
man beim letzten ankommt. Dann sind alle Threads zu Ende.
Bereits positiv getestete kann man so auslassen.
Habs vorhin mal mit Mutexen und mit einem Array versucht. Du hattest
recht, Array ist in diesem Fall sehr viel schneller. CPU Auslastung mit
Mutex ca. 93%, mit Array 99%
Hallo,
was wollt ihr immer bloß mit Mutexen? Dafür nimmt man eine Critical
Section oder deren Abkömmlinge, die sind deutlich leichtgewichtiger.
Und in Multi-Core-Umgebungen garantiert Dir niemand mehr, dass
Integer-Zugriffe atomar sind. Außerdem garantiert kein Compiler, dass
"++" wirklich atomar ist. Schließlich kann durch Optimierung die
Varaiable in einem Register gehalten werden, dort erhöht werden und erst
später zurückgeschrieben werden. Ein anderer Thread auf einem zweiten
Core würde dann ein inkonsistentes Ergebnis lesen!
Dafür gibt es dann die Api-Funktionen InterlockedIncrement/Decrement
usw., die sollte man in jedem Fall dafür anwenden (sind dann ja auch
Lock-Free).
Bei der Thread-Programmierung bietet sich C++ (von mir aus auch C#)
geradezu an, da man in einer (Thread)-Klasse alle threadspezifischen
Variablen sehr gut kapseln kann und alle threadübergreifenden Variablen
als statische Klassenvariablen deklarieren kann, die dann durch eine CS
geschützt werden.
Der Code wird dadurch jedenfalls wesentlich übersichtlicher und besser
verwaltbar.
In reinem C hat man dann solche Konstrukte wie TLS oder Arrays,
spätestens wenn man dynamisch neue Threads hinzufügt oder entfernt
wird's echt haarig.
Nur so als Anregung...
Gruß,
Oliver R. schrieb:
> Hallo,>> was wollt ihr immer bloß mit Mutexen? Dafür nimmt man eine Critical> Section oder deren Abkömmlinge, die sind deutlich leichtgewichtiger.
Lt. http://de.wikipedia.org/wiki/Mutex
* Ein kritischer Abschnitt (engl. critical section oder critical
region) ist derjenige Teil im ausführbaren Code, in dem ein wegen des
Mutex ungestörter Zugriff auf Daten erfolgt. Ein kritischer Abschnitt
darf nicht unterbrochen werden
* von einem anderen Thread, der auf dieselben über Mutex
geschützten Daten zugreifen will,
* von einem anderen Thread, der den Mutex-Zugriff möglicherweise
unzulässig verlängern würde, da er den zugreifenden Thread längere Zeit
unterbricht.
> Und in Multi-Core-Umgebungen garantiert Dir niemand mehr, dass> Integer-Zugriffe atomar sind.
Zuweisung an char aber sicher.
> Außerdem garantiert kein Compiler, dass> "++" wirklich atomar ist.
Hat auch niemand behauptet.
Ich sagte, wenn man es mit einer Variablen löst, die von
allen Threads hochgezählt wird, dann braucht man einen Mutex.
Eben weil das nicht atomar ist.
> ...> Bei der Thread-Programmierung bietet sich C++ (von mir aus auch C#)> geradezu an, da man in einer (Thread)-Klasse alle threadspezifischen
Stimmt.
> Variablen sehr gut kapseln kann und alle threadübergreifenden Variablen> als statische Klassenvariablen deklarieren kann, die dann durch eine CS> geschützt werden.
Oder durch einen Mutex, je nachdem, wie man das Biest nennt,,,
> Der Code wird dadurch jedenfalls wesentlich übersichtlicher und besser> verwaltbar.> In reinem C hat man dann solche Konstrukte wie TLS oder Arrays,> spätestens wenn man dynamisch neue Threads hinzufügt oder entfernt> wird's echt haarig.> Nur so als Anregung...>> Gruß,
Oliver R. schrieb:
> was wollt ihr immer bloß mit Mutexen? Dafür nimmt man eine Critical> Section oder deren Abkömmlinge, die sind deutlich leichtgewichtiger.
Oder das, was für sehr kurze Locks um Variable herum tatsächlich
sinnvoll ist, weil es dabei fast keine Resourcen frisst: Spinlocks.
Hallo,
>>>> was wollt ihr immer bloß mit Mutexen? Dafür nimmt man eine Critical>> Section oder deren Abkömmlinge, die sind deutlich leichtgewichtiger.>> Lt. http://de.wikipedia.org/wiki/Mutex> * Ein kritischer Abschnitt (engl. critical section oder critical> region) ist derjenige Teil im ausführbaren Code, in dem ein wegen des> Mutex ungestörter Zugriff auf Daten erfolgt. Ein kritischer Abschnitt> darf nicht unterbrochen werden>> * von einem anderen Thread, der auf dieselben über Mutex> geschützten Daten zugreifen will,> * von einem anderen Thread, der den Mutex-Zugriff möglicherweise> unzulässig verlängern würde, da er den zugreifenden Thread längere Zeit> unterbricht.>
Schön abgeschrieben, aber da wir hier WINDOWS-Programmierung machen
besteht sehr wohl ein großer Unterschied zwischen einer CriticalSection
und einem Mutex. Der Bedeutung nach ist eine CriticalSection nach
natürlich auch ein Mutex, bei der Windows-Programmierung unterscheidet
man aber sehr wohl zwischen beiden (CreateMutex(...) /
InitializeCriticalSectionAndSpinCount(...))
>>>> Und in Multi-Core-Umgebungen garantiert Dir niemand mehr, dass>> Integer-Zugriffe atomar sind.>> Zuweisung an char aber sicher.
Nein, sind sie nicht. Es gibt im X86-ASM durchaus atomare Funktionen,
aber niemand garantiert, dass der Compiler sie auch verwendet. Der
"++"-Operator war nur ein Beispiel.
Gruß,
Oliver R. schrieb:
> Nein, sind sie nicht. Es gibt im X86-ASM durchaus atomare Funktionen,> aber niemand garantiert, dass der Compiler sie auch verwendet.
Und genau deshalb existieren die erwähnten Interlocked... Funktionen.
A. K. schrieb:
> Oliver R. schrieb:>>> was wollt ihr immer bloß mit Mutexen? Dafür nimmt man eine Critical>> Section oder deren Abkömmlinge, die sind deutlich leichtgewichtiger.>> Oder das, was für sehr kurze Locks um Variable herum tatsächlich> sinnvoll ist, weil es dabei fast keine Resourcen frisst: Spinlocks.
Ja, wenn Du Treiber schreibst, dann kannst Du das machen. Macht hier
aber gerade keiner! -> Kernel Mode
Gruß,
Oliver R. schrieb:
> Ja, wenn Du Treiber schreibst, dann kannst Du das machen. Macht hier> aber gerade keiner! -> Kernel Mode
Spinlocks sind eine allgemeine Synchronisationstechnik. Dafür braucht
man keinen Kernel-Modus. Der Unterschied zu System-Mutexen besteht
darin, dass konkurrierende Threads in der kritischen Phase auf
Applikationsebene aktiv pollen, nicht sich wie in Mutexen schlafen
legen. Wenn es, wie bei Variablenzugriffen, nur um ein paar Takte geht
ist das viel billiger als jeder Systemcall.
Gibt natürlich ein paar Dinge die man beachten sollte. Wie Cache-Lines
und kleine Delays (ggf. hinting NOPs genannt) um bei SMT/Hyperthreading
dem SMT-Kollegen nicht auf den Füssen zu stehen.
A. K. schrieb:
> Oliver R. schrieb:>>> Ja, wenn Du Treiber schreibst, dann kannst Du das machen. Macht hier>> aber gerade keiner! -> Kernel Mode>> Spinlocks sind eine allgemeine Synchronisationstechnik. Dafür braucht> man keinen Kernel-Modus.
Ich hätte auch schreiben können, das WINDOWS nur im Kernel-Modus dafür
Funktionen bereitstellt. Es gibt wohl auch C/C++-Implementationen, die
kommen aber ohne inline-ASM auch nicht aus. Mir wäre nicht wohl dabei.
Gruß,
> Ich hätte auch schreiben können, das WINDOWS nur im Kernel-Modus dafür> Funktionen bereitstellt.
Dann lies die Doku zu "InitializeCriticalSectionAndSpinCount".
Die Funktion heißt nicht umsonst so.
... schrieb:
>> Ich hätte auch schreiben können, das WINDOWS nur im Kernel-Modus dafür>> Funktionen bereitstellt.>> Dann lies die Doku zu "InitializeCriticalSectionAndSpinCount".> Die Funktion heißt nicht umsonst so.
Manchmal frage ich mich, warum ich hier überhaupt noch schreibe.
Eine Critical Section im User Mode ist halt was ganz anderes als ein
Spin-Lock im Kernel-Mode. Das ist auch ganz klar so im MSDN beschrieben.
Aber drei Punkte stehen bekanntlich ja auch für Blindheit.
Gruß,
> Oliver R. schrieb:>> > Nein, sind sie nicht. Es gibt im X86-ASM durchaus atomare Funktionen,> > aber niemand garantiert, dass der Compiler sie auch verwendet.>> Und genau deshalb existieren die erwähnten Interlocked... Funktionen.
Wobei es jedoch kein InterlockedRead() bzw. InterlockedWrite() gibt,
weil Microsoft (inkl. Compiler) den Lese-/Schreibzugriff auf (aligned)
32 bit Variablen als atomar garantiert.