Forum: PC-Programmierung Verhindern, das Funktion zweifach läuft


von meister lämpchen (Gast)


Lesenswert?

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.

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Hi

Mutex. Wie das im Detail aussieht hängt von deinem OS bzw. deiner 
Treadbibliothek ab. POSIX -> pthread_mutex_*

Matthias

von Marcus B. (raketenfred)


Lesenswert?

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!

von Peter II (Gast)


Lesenswert?

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?

von ... (Gast)


Lesenswert?

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, ...

von meister lämpchen (Gast)


Lesenswert?

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.

von Turbo J (Gast)


Lesenswert?

Du brauchst eine eine "Critical Section". Weitere hilfreiche 
Google-Suchbegriffe wären "Mutex" und "Semaphore".

von Peter II (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von ... (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

... 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.

von meister lämpchen (Gast)


Lesenswert?

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?

von (prx) A. K. (prx)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

... 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.

von Peter II (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

A. K. schrieb:
> Anderer Name für das gleiche Grundprinzip, anders implementiert.

nein, dann genau das was du geschrieben hast kann man damit machen:

http://msdn.microsoft.com/en-us/library/ms683476(v=VS.85).aspx

von (prx) A. K. (prx)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von Frank E. (Firma: Q3) (qualidat)


Lesenswert?

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 ...

von (prx) A. K. (prx)


Lesenswert?

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. ;-)

von Peter II (Gast)


Lesenswert?

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.

von meister lämpchen (Gast)


Lesenswert?

Peter II schrieb:
> it
> einem Mutex/CriticalSection sollte sich das Problem für meister lämpchen
> recht gut lösen lassen.

Leider nicht so richtig:
1
  LPCRITICAL_SECTION lock = InitializeCriticalSection(&lock);
2
  EnterCriticalSection( &lock );

wenn das am Anfang der Funktion steht, gits Fehler. lock ist nicht 
initialisiert.

von Peter II (Gast)


Lesenswert?

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.

von ... (Gast)


Lesenswert?

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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.