Hallo,
ich habe folgendes Problem: Durch verschiedene Programmkonstellationen
kann es vorkommen, dass zwei Threads gleichzeitig gestartet werden, die
jeweils eine Funktion mit verschiedenen Parametern aufrufen. (Thread A
ruft Fuktion mit Parameter 1 auf und Thread B startet die Funktion mit
Parameter 2)
Das ganze geht manchmal so schnell, dass die Funktion nochmal aufgerufen
wird, während sie noch läuft. Rausgefunden habe ich das so: Globale
Variable c=0, in erster Funktionszeile c++; und am ende c--; Wenn dann
nicht c==0 kommt, muss die Funktion nochmal aufgerufen worden sein,
bevor sie beendet wurde.
Wie kann ich das verhindern? Der Funktionsaufruf müsste in einer
Warteschleife warten, bis der erste Durchlauf beendet wurde.
ich würde die Funktion einfach als Atomar definieren.
D.h. solange wie diese Funktion läuft, kann sie nicht durch einen
anderen Thread der zu dem Prozess gehört unterbrochen werden.
Ansonsten nur eine Bastellösung wie du mit dem c vorgeschlagen hast-
vorsicht, da muss der Zugriff auf das c aber auch atomar ablaufen!
meister lämpchen schrieb:> ich habe folgendes Problem: Durch verschiedene Programmkonstellationen> kann es vorkommen, dass zwei Threads gleichzeitig gestartet werden, die> jeweils eine Funktion mit verschiedenen Parametern aufrufen. (Thread A> ruft Fuktion mit Parameter 1 auf und Thread B startet die Funktion mit> Parameter 2)
die Frage ist erstmal wo das Problem dabei ist. Es wenn die funktion
keine globalen resourcen nutzt dann ist es auch kein Problem das die
funtion mehrfach gleichzeitg läuft.
Also erstmal die Frage: Warum hast du ein Problem damit das du funktion
nicht mehrfach laufen darf?
Peter II schrieb:> die Frage ist erstmal wo das Problem dabei ist. Es wenn die funktion> keine globalen resourcen nutzt dann ist es auch kein Problem das die> funtion mehrfach gleichzeitg läuft.
Sowas nennt sich dann reentrant
(http://de.wikipedia.org/wiki/Eintrittsinvarianz).
Ansonsten, Mutex wurde ja schon genannt. Weitere Stichwörten wären z.B.
semaphore, critical section, lock, ...
Aber wie baue ich irgendetwas vom genannten in mein C-Programm ein?
Das Problem ist folgendes: Ich steuere ein IO-Interface an (über
CAN-Bus). Wenn beide Threads A und B gleichzeitug gestartet werden, wird
die Funktion "Lampe 1 einschalten" und "Lampe 2 einschalten"
gleichzeitig ausgeführt. Und in der Schalt-Funktion kommt dann
(manchmal) etwas durcheinander.
Um Lampe 1 zu schalten, muss das aktuelle Ausgangsbyte mit dem Bit, das
der Lampe 1 entspricht verknüpft und ausgegeben werden. Wenn
gleichzeitig die Lampe 2 in der Schaltfunktion wütet, wird das Bit mit
einer falschen Grundmaske maskiert.
Ich möchte also zum testen, dass der Thread die Funktion so lange nicht
startet, bis die eine durchgelaufen ist.
meister lämpchen schrieb:> Ich möchte also zum testen, dass der Thread die Funktion so lange nicht> startet, bis die eine durchgelaufen ist.
das starten der funktion ist kein problem. du musst dann nur warten.
Bei windows könnte man es so machen.
handle lock = createcriticalsection;
void send() {
EnterCriticalSection( &lock );
Sende();
LeaveCriticalSection( &lock );
}
wenn jetzt 2 Thread gleicheztig die funktion aufrufen, dann wartet der
2.Thread bei EnterCriticalSection bis der 1.thread LeaveCriticalSection
ausgeführt hat.
Wenn mehrere Threads gemeinsame eine Resource nutzen, wie etwas ein CAN
Interface, und dieses Interface nicht selbst dafür sorgt, dass es
mehrfache Verwendung serialisiert, dann muss du das tun. Und dafür sind
die bereits genannten Verfahren vorgesehen.
Ist ein Standardverfahren bei nebenläufiger Programmierung.
Grundlagenwissen.
Willst du das nun schlicht und einfach nutzen, oder willst du ein
solches Verfahren implementieren?
Nutzen: verrate mal, worin/worauf du programmierst. Jedes Betriebssystem
bietet dir APIs dafür an.
Implementieren: oops, da gehts etwas arg in die Tiefe, bis runter auf
Aspekte von Rechnerarchitekturen, wenn tatsächlich PCs gemeint sind. Auf
C Ebene kommst da nicht weiter, da die verwendeten Maschinenbefehle
entscheidend sind. Tipp: hier gehts erstmal nicht weiter, Sackgasse,
denn so wie du dir das vorstellst geht es eben nicht.
A. K. schrieb:> Implementieren: oops, da gehts etwas arg in die Tiefe, bis runter auf> Aspekte von Rechnerarchitekturen, wenn tatsächlich PCs gemeint sind. Auf> C Ebene kommst da nicht weiter, da die verwendeten Maschinenbefehle> entscheidend sind. Tipp: hier gehts erstmal nicht weiter, Sackgasse.
Bei M$ käme man da schon weiter. Läßt sich über die Interlocked...
Funktionen implementieren. Beispiele dafür kann man z.B. in der
pthreads-win32 Lib sehen. Was die MS-API sonst so zum Theme
Synchronisation zu bieten hat findet man u.a. hier:
http://msdn.microsoft.com/de-de/library/ms686360.aspx
... schrieb:> Bei M$ käme man da schon weiter. Läßt sich über die Interlocked...> Funktionen implementieren.
Ist aber letztlich auch nur einer jener APIs, von denen ich sprach. Nur
etwas weiter unten als Mutexe. Warum das Rad neu erfinden? Hier geht es
um Mutexe, und die gibts ja schliesslich auch fertig.
A. K. schrieb:> Ist ein Standardverfahren bei nebenläufiger Programmierung.> Grundlagenwissen.>> Willst du das nun schlicht und einfach nutzen, oder willst du ein> solches Verfahren implementieren?
Ich möchte es eigentlich nur nutzen, sofern es (relativ einfach) möglich
ist. Ich programmiere in C unter Windows 7 mit einer relativ unbekannten
Entwicklungsumgebung von National Instruments.
Bei HANDLE lock = CreateCriticalSection(); kommt der Fehler "missing
prototype". Wo ist CreateCriticalSection definiert? Wo bekomme ich den
Header dafür her?
meister lämpchen schrieb:> Wo bekomme ich den Header dafür her?
Indem du die Doku von MS für diese Funktion spickst. Da steht
üblicherweise drin, welcher Header zuständig ist.
... schrieb:> Bei M$ käme man da schon weiter. Läßt sich über die Interlocked...> Funktionen implementieren.
Wobei da dann die Frage im Raum stünde, ob die Funktion wirklich so kurz
und knapp ist, dass man sie mit Mutex-Verfahren mit aktivem Warten
bedienen sollte. Denn darauf läufts damit ja raus. Wenn da Millisekunden
mit Däumchendrehen draufgehen geht es anders effizienter.
Überdies sollte man auch hier etwas über Architektur wissen, um sich
nicht selber massiv auf den Füssen zu stehen. Denn wenn eine solche
Variable in der gleichen Cache-Line landet wie eine vom so geschützten
Code häufiger genutze normale Variable, dann drückt das mächtig auf die
Performance. Über derartige Effekte (gibt noch mehr) sind schon manche
bös gestolpert.
A. K. schrieb:> Wobei da dann die Frage im Raum stünde, ob die Funktion wirklich so kurz> und knapp ist, dass man sie mit Mutex-Verfahren mit aktivem Warten> bedienen sollte. Denn darauf läufts damit ja raus. Wenn da Millisekunden> mit Däumchendrehen draufgehen geht es anders effizienter.
CriticalSection soll effentiver als Mutexe sein, aus dem grund habe ich
das vorgeschlagen.
meister lämpchen schrieb:> CreateCriticalSection definiert? Wo bekomme ich den> Header dafür her?
kann doch nicht so schwer sein, du solltest dir eh die doku durchlesen
bevor man funktionen das 1.Mal verwendet.
http://msdn.microsoft.com/en-us/library/ms683472(v=vs.85).aspx
sorry heist auch InitializeCriticalSection
Peter II schrieb:> CriticalSection soll effentiver als Mutexe sein, aus dem grund habe ich> das vorgeschlagen.
Ist letztlich auch eine Mutex im allgemeinen Sinn. Ein anderer Name für
das gleiche Grundprinzip, anders implementiert. Aus schon erwähnten
Gründen gibt es heute oft mehrere Wege nach Rom, d.h. verschiedene APIs
für das gleiche Prinzip, mit unterschiedlichen Nebeneffekten. Für
Laufzeiten im Submikrosekundenbereich wie atomaren Variablenoperationen
sind klassische OS-Mutexe völliger Irrsinn, im Millisekundenbereich ist
andererseits aktives Warten Blödsinn.
Peter II schrieb:> nein, dann genau das was du geschrieben hast kann man damit machen:
Klar. Aber das ist kein Widerspruch. Für mich ist der Begriff "Mutex"
ein allgemeiner Begriff bei Betriebssystemen, kein bestimmter mehr oder
weniger zufällig so heissender API von Microsoft. Demzufolge ist für
mich diese Critical Section ebenfalls eine Mutex, nur mit anderem Namen.
Entstanden aus der erwähnten Problematik heraus, als mit SMT und
Multicores die klassischen OS-Mutexe ineffizient wurden.
A. K. schrieb:> Ist letztlich auch eine Mutex im allgemeinen Sinn. Ein anderer Name für> das gleiche Grundprinzip, anders implementiert.
nachtrag:
eine CriticalSection gibt es nur in einem Prozess sie muss also nicht im
Kernel implementiert sein. Ein Mutex kann Prozessübergreifend eingesetzt
werden damit muss immer der Kernel belästig werden.
Peter II schrieb:> eine CriticalSection gibt es nur in einem Prozess sie muss also nicht im> Kernel implementiert sein. Ein Mutex kann Prozessübergreifend eingesetzt> werden damit muss immer der Kernel belästig werden.
Yep, aber du reitest hier Microsofts Namenskonvention aus. Die ist für
mich nicht massgeblich. Sind für mich einfach nur verschiedene Sorten
Mutexe, mit unterschiedlichen Eigenheiten. Ich erlaube mir da eine etwas
abgehobenere Betrachtungsweise.
Vlt. kann man das Problem auch mit einem anderen Programmierstil
entschärfen. Dein Beispiel mit dem auszugebenden Byte würde ich so
lösen:
- im Speicher für jedes der Bits einen eigenen Puffer halten
- eine Funktion schreiben, die die einzelnen Bits unabhängig voneinander
setzen kann
- nach jeder Änderung an einem der Bitpuffer (oder zyklisch) diese Bits
zu einem Byte "zusammenkehren" und in das Ausgaberegister kopieren
So dürfte es eigentlich nicht zu "Kuddelmudel" kommen ...
Frank Esselbach schrieb:> So dürfte es eigentlich nicht zu "Kuddelmudel" kommen ...
Kommt drauf an, was du darunter verstehst. Es können dann nämlich
Zustände auf den Ausgängen auftauchen, die in dieser Kombination zu
keinem einzigen Zeitpunkt im Programm aufgetreten sind. Es sei denn du
schützt das Zusammenkehren der Bits durch eine Mutex. ;-)
A. K. schrieb:> sind. Es sei denn du> schützt das Zusammenkehren der Bits durch eine Mutex. ;-)
aber dafür gibt es atomar funktionen wie Interlocked...
Aber ich glaube dieser Ansatz ist hier ein wenig zu kompliziert. Mit
einem Mutex/CriticalSection sollte sich das Problem für meister lämpchen
recht gut lösen lassen.
meister lämpchen schrieb:> Leider nicht so richtig:> LPCRITICAL_SECTION lock = InitializeCriticalSection(&lock);> EnterCriticalSection( &lock );> wenn das am Anfang der Funktion steht, gits Fehler. lock ist nicht
das
LPCRITICAL_SECTION lock = InitializeCriticalSection(&lock);
!!!!!!!!darf auch nicht in der funktion stehen!!!!!!!!!!
scheinbar hast du noch nicht mal verstanden wie das ganze funktionieren
soll. Bitte erst doku lesen.
Das ist grundlagenwissen wenn man multithreaded programmiert.
meister lämpchen schrieb:> Leider nicht so richtig:> LPCRITICAL_SECTION lock = InitializeCriticalSection(&lock);> EnterCriticalSection( &lock );
Auweia. Ist es tatsächlich so schwer mal eine Duco zu lesen und
vielleicht auch mal zwei, drei Links weiter zu klicken?
So misarabel ist MSDN doch nu wirklich nicht.
http://msdn.microsoft.com/de-de/library/ms686908.aspx