Forum: PC-Programmierung Frage zu Threads


von Stefan (Gast)


Lesenswert?

Hallo an alle,

Ich habe eine Frage zu Threads. Und zwar ich habe zwei Threads: THREAD_A 
und THREAD_B.

In beiden Threads soll auf die serielle Schnittstelle geschrieben 
werden. Mir ist klar, dass ich vor einem Zugriff auf die Schnittstelle 
(zB fprintf) den kritischen Bereich mit einem Mutex sperren muss.

Nun zu meiner eigenen Frage, ich kann ja einen Stream, eben /dev/tts/0, 
mehrfach öffnen. Ich musss nur verhindern, dass nicht gleichzeitig 
zugegeriffen wird(eben mit dem Mutex).

Ist es besser so, oder einen gloabel Filedeskriptor für den Stream??

Danke im Voraus

mfg Stefan

von Stephan M. (stephanm)


Lesenswert?

Stefan schrieb:
> Nun zu meiner eigenen Frage, ich kann ja einen Stream, eben /dev/tts/0,
> mehrfach öffnen. Ich musss nur verhindern, dass nicht gleichzeitig
> zugegeriffen wird(eben mit dem Mutex).
>
> Ist es besser so, oder einen gloabel Filedeskriptor für den Stream??

Hm, ohne genau zu wissen was Du da machst ist es schwierig, diese Frage 
zu beantworten. Tendenziell würde ich sagen: Ein globales Filehandle 
könnte besser sein.

Allerdings frage ich mich, warum Du mit zwei Threads auf die serielle 
Schnittstelle zugreifst. Wenn ich mich recht entsinne wars der 
Kernel-Guru Alan Cox, der mal geäußert hat, dass Threads was für Leute 
sind, die keine State Machines (sauber) implementieren können. Und damit 
hat er möglicherweise auch garnicht so unrecht :-)

Stephan

von P. S. (Gast)


Lesenswert?

Stephan M. schrieb:

> Wenn ich mich recht entsinne wars der
> Kernel-Guru Alan Cox, der mal geäußert hat, dass Threads was für Leute
> sind, die keine State Machines (sauber) implementieren können. Und damit
> hat er möglicherweise auch garnicht so unrecht :-)

Alte Ausrede fuer Leute, die mit Threads nicht umgehen koennen... leider 
die Mehrheit und die hat ja immer recht :-/

von Stefan (Gast)


Lesenswert?

Hallo,

Nun ich denke genau Threads sind für mein Problem die Lösung.
Und zwar im THREAD_A scanne ich alle 5 Sekunden scanne ich einen Ordner 
und bei einer Änderung sende ich etwas über die serielle Schnittstelle.

Der THREAD_B liest zyklisch etwas auf der seriellen Schnittstelle ein, 
und bei Empfang eines Kommandos antwortet er falls notwendig.

Hier denke ich sind Threads geeignet.

von Sven P. (Gast)


Lesenswert?

Stefan schrieb:
> Nun ich denke genau Threads sind für mein Problem die Lösung.
> Und zwar im THREAD_A scanne ich alle 5 Sekunden scanne ich einen Ordner
> und bei einer Änderung sende ich etwas über die serielle Schnittstelle.
Da wäre FAM (file alteration monitor) aber sinnvoller, als ein Thread.

> Der THREAD_B liest zyklisch etwas auf der seriellen Schnittstelle ein,
> und bei Empfang eines Kommandos antwortet er falls notwendig.
Da wäre select() sinnvoll.

> Hier denke ich sind Threads geeignet.
Naajaa..

von Stephan M. (stephanm)


Lesenswert?

Stefan schrieb:
> Nun ich denke genau Threads sind für mein Problem die Lösung.
> Und zwar im THREAD_A scanne ich alle 5 Sekunden scanne ich einen Ordner
> und bei einer Änderung sende ich etwas über die serielle Schnittstelle.
>
> Der THREAD_B liest zyklisch etwas auf der seriellen Schnittstelle ein,
> und bei Empfang eines Kommandos antwortet er falls notwendig.
>
> Hier denke ich sind Threads geeignet.

Ja. Auf jeden Fall "geeignet" im Sinne von "Du kommst ans Ziel".

Auf Grund der Beschreibung klingt die Sache für mich eher nach einem 
klassischen Anwendungsfall für Single-Threaded mit select(2), außer da 
spielen noch andere Anforderungen mit rein.

Peter Stegemann schrieb:
>> Wenn ich mich recht entsinne wars der
>> Kernel-Guru Alan Cox, der mal geäußert hat, dass Threads was für Leute
>> sind, die keine State Machines (sauber) implementieren können. Und damit
>> hat er möglicherweise auch garnicht so unrecht :-)
> Alte Ausrede fuer Leute, die mit Threads nicht umgehen koennen...

Ich denke Alan kann das :-)

Stephan

von Stefan (Gast)


Lesenswert?

Danke für den Tipp mit dem FAM. Leider gibt es kein Paket für meine 
Zielhardware. Ich weiß noch nicht ob mir das Crosscompiling antue.

Welchen Vorteil hat ein FAM im Gegensatz zur manuellen Implementierung? 
Derzeit prüfe ich alle 5 Sekunden die Modifizierungszeit des Ordners. 
Bei einer Änderung zur vorigen Prüfung parse ich alle Dateien im Ordner.

Optimal wäre hier ein Timer, doch leider gibt es so etwas unter C nicht 
:(

Sven P. schrieb:
> Da wäre select() sinnvoll.

Dazu habe ich eine Frage. Und zwar ich lese Strings zyklisch von der 
seriellen mit getline() ein. Wenn ich das mit select() mache, könnte es 
ja sein, dass die Zeile noch nicht vollständig übertragen worden ist, 
select(() aber schon meldet, dass etwas gekommen ist, oder??

Hier am besten wieder mit getline() arbeiten und wenn die Zeile noch 
nicht vollständig übertragen ist, liefert getline() einfach noch keine 
Zeile??

von Rolf Magnus (Gast)


Lesenswert?

> Danke für den Tipp mit dem FAM. Leider gibt es kein Paket für meine
> Zielhardware. Ich weiß noch nicht ob mir das Crosscompiling antue.

Naja, FAM setzt auch nur auf inotify auf. Das kannst du auch direkt 
benutzen.

> Welchen Vorteil hat ein FAM im Gegensatz zur manuellen Implementierung?
> Derzeit prüfe ich alle 5 Sekunden die Modifizierungszeit des Ordners.

Bei FAM/inotify meldest du an, welche Verzeichnisse überwacht werden 
sollen, und sobald sich was ändert, wirst du benachrichtigt. Da der 
Kernel ja eh weiß, wenn sich was geändert hat, muß er nicht pollen.
Die Wikipedia hat eine ganz gute Kurzbeschreibung von inotify:
http://en.wikipedia.org/wiki/Inotify

> Bei einer Änderung zur vorigen Prüfung parse ich alle Dateien im Ordner.

Auch die, die sich nicht geändert haben?

> Optimal wäre hier ein Timer, doch leider gibt es so etwas unter C nicht
> :(

Klar gibt's das.
man setitimer
man timer_create

> Dazu habe ich eine Frage. Und zwar ich lese Strings zyklisch von der
> seriellen mit getline() ein. Wenn ich das mit select() mache, könnte es
> ja sein, dass die Zeile noch nicht vollständig übertragen worden ist,
> select(() aber schon meldet, dass etwas gekommen ist, oder??

Ja.

> Hier am besten wieder mit getline() arbeiten und wenn die Zeile noch
> nicht vollständig übertragen ist, liefert getline() einfach noch keine
> Zeile??

Wenn deine Schnittstelle nicht auf non-blocking eingestellt ist, wird 
die Funktion vermutlich so lange blockieren, bis eine ganze Zeile 
angekommen ist. Nun ist es aber auch nicht wirklich schwierig, 
stattdessen read() zu verwenden und das Zeilenende selber zu suchen.

von Stefan (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Bei FAM/inotify meldest du an, welche Verzeichnisse überwacht werden
> sollen, und sobald sich was ändert, wirst du benachrichtigt. Da der
> Kernel ja eh weiß, wenn sich was geändert hat, muß er nicht pollen.
> Die Wikipedia hat eine ganz gute Kurzbeschreibung von inotify:
> http://en.wikipedia.org/wiki/Inotify

Danke für den Hinweiß. Ich werde mal ein 2.6er Kernelsystem aufsetzten 
und mich mit inotify beschäftigen.

Rolf Magnus schrieb:
> Klar gibt's das.
> man setitimer
> man timer_create

Danke auch für diesen Hinweiß. Die Timerfunktion löse ich mit einem 
Signalhandler der bei SIGVTALRM triggert.

Rolf Magnus schrieb:
> Wenn deine Schnittstelle nicht auf non-blocking eingestellt ist, wird
> die Funktion vermutlich so lange blockieren, bis eine ganze Zeile
> angekommen ist. Nun ist es aber auch nicht wirklich schwierig,
> stattdessen read() zu verwenden und das Zeilenende selber zu suchen.

Die Schnittstelle ist auf non-blocking eingestellt. Sollte also 
funktionieren =)

Rolf Magnus schrieb:
> Auch die, die sich nicht geändert haben?

Derzeit ja. Mit inotify komme ich nicht zu der Datei die sich im 
beobachteten Ordner verändert hat, oder??

Danke für die super Hilfe!!

lg Stefan

PS: Die Zitat-Funktion finde ich super!

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Ich würde das so machen das der Thread welcher das Lesen der 
Schnittstelle übernimmt, einfach von dem anderem Thread eine Nachricht 
erhält: Ich möchte den String XYZ senden.
Der "Empfang und Sende Thread" kann dann diesen String über die 
Schnittstelle senden sobald Zeit dafür ist ohne das es zu 
Zugriffsproblemen kommt.

von Rolf Magnus (Gast)


Lesenswert?

> Mit inotify komme ich nicht zu der Datei die sich im beobachteten Ordner
> verändert hat, oder??

Doch. Wenn du ein ganzes Verzeichnis überwachst, bekommst du bei 
Änderung den Namen der Datei, die sich geändert hat und eine Maske, die 
angibt, um welche Art von Änderung es sich handelt.
Dazu empfehle ich die man-Page von inotify:
1
man 7 inotify

Da ist das ganze ausführlich erklärt.

von Klaus W. (mfgkw)


Lesenswert?

Läubi .. schrieb:
> Ich würde das so machen das der Thread welcher das Lesen der
> Schnittstelle übernimmt, einfach von dem anderem Thread eine Nachricht
> erhält: Ich möchte den String XYZ senden.
> Der "Empfang und Sende Thread" kann dann diesen String über die
> Schnittstelle senden sobald Zeit dafür ist ohne das es zu
> Zugriffsproblemen kommt.

Ja, oder gleich einen dritten Thread:
zwei wie gehabt, und wenn sie etwas senden wollen geht das an den
dritten. Der sendet über RS232 und verteilt ggf. zurück die Antworten.

Ich finde die Aufteilung auf mind. 2 Threads für so etwas alleine
deshalb attraktiver, weil erstens die Funktionalitäten sauberer
voneinander getrennt sind als wenn alles in eimem Brei um
select herum vermatscht wird, und zweitens werden auch automatisch
mehrere Kerne/CPUs genutzt.

von Stefan (Gast)


Lesenswert?

Danke für all die Tipps!!

Für die Interthread-Kommunikation gibt es ja mehrere Wege. Ich habe mich 
für Message Queues entschieden. Ist das so sinnvoll?

von Stefan (Gast)


Lesenswert?

Beim SerialThread denke ich ist jedoch select() nicht sinnvoll. Denn 
dieser Thread soll ja schnell auf Nachrichten von der MessageQueue 
können.

Hier ist pollen der Schnittstelle mit getline() doch besser, oder?

von Purzel H. (hacky)


Lesenswert?

Pollen ? Sonst noch was ?

von Klaus W. (mfgkw)


Lesenswert?

Vielleicht meint er nicht pollen, sondern blockierendes Lesen?
Sonst kann er kaum schneller reagieren als mit select().
Hört sich jedenfalls nicht ganz stimmig an (Autor:  Stefan (Gast)).

von Stefan (Gast)


Lesenswert?

Also ich habe einen serialThread(). Der ist zuständig für das Senden und 
Empfangen über die Schnittstelle. Die Daten reicht der Thread mit 
Messages zu den Datenverarbeitenden Threads. Was gesendet wird empfängt 
der Thread per Messages.

Hier mal ein Pseudocode:
1
while(1)
2
{
3
   if(readline())
4
     sendMessage(line);
5
6
   if(receivedMessage())
7
     writeSerial(msg)
8
}

Wenn ich blockierend lese oder es mit select löse, Timeout zB 500ms, 
kann writeSerial() nicht schnell reagieren. Das meine ich.

Hier wären doch zwei Threads notwenig. Einer zum Senden und einer zum 
Schreiben. Jedoch bin ich somit wieder beim Usprungsproblem.

von Klaus W. (mfgkw)


Lesenswert?

Sehe ich das richtig, daß sowohl sendMessage(line) als
auch receivedMessage() nichtblockierend sind und du in
einer Endlosschleife beide abwechselnd abfragst, nur um in
aller Regel festzustellen, daß nichts los ist?

von Klaus W. (mfgkw)


Lesenswert?

nur zur Sicherheit:
Wen du bei select() einen timeout angibst, dann heisst das nicht,
daß select() wirklich solange wartet.
Vielmehr wartet es höchstens solange, springt aber schon
vorher zurück, wenn einer der übergebenen fd heiß wird.
Genau dafür ist select() ja da.

von Stefan (Gast)


Lesenswert?

Hallo Klaus,

Genau so ist es. Deswegen dachte ich zwei Threads, und jeweils eine 
blockierende Funktion. Jedoch weiß ich nicht wie es sich verhält, wenn 
readline() blockiert, und eine Message zum sendThread() kommt und der 
senden will.

von Klaus W. (mfgkw)


Lesenswert?

Es ist natürlich Unfug, in dem Thread, der für den anderen
die Antwort senden soll, zu blockieren.
Ich sehe auch nicht ein, wieso ein Thread unbedingt
zwei grundverschiedene Dinge machen soll.

Deshalb mein Vorschlag mit den 3 Threads: einer kümmert
sich nur ums Senden und bekommt seine Aufträge von den
anderen beiden.

Andere Variante: beide Threads können über die serielle
schreiben und stimmen sich ggf. mit einem Mutex ab,
oder dritte Variante: select(), wobei ich nicht verstehe,
was daran langsam sein soll.

Eine Endlosschleife mit nonblocking fd ist aber mit Sicherheit
die ungeschickteste Lösung.

von Sven P. (Gast)


Lesenswert?

Und die Threads wollen auch koordiniert werden, was wieder Mutexen oder 
Semaphoren oder sonstigen Overhead anschleppt. Grad das gegenseitige 
Signalisieren ist weniger trivial, als man annehmen mag.

von Stefan (Gast)


Lesenswert?

Hab die 2. Nachricht noch gesehen.

Und zwar im readThread arbeite ich mit select(). Wenn etwas reinkommt, 
bricht select() ab und ich kann auf die eingehende Zeile reagieren.

Der sendThread empfängt eine Nachricht (blockierend) und will senden, 
dadurch bricht select im readThread ab, und die Schnittstelle ist frei.

Hab ich das so richtig verstanden?

von Klaus W. (mfgkw)


Lesenswert?

@Sven:
je nachdem, wie sie plaudern.
Der dritte, der nur versendet, kann doch recht problemlos
immer erst eine Nachricht komplett schicken, bevor er die
andere anfasst.

Man muß es sicher nicht mit dem dritten Thread machen,
100% CPU-Last zum Pollen ist bestimmt nicht der beste Weg.
1
Variante       einfach    modular   effizient 1 Kern   effizient Mehrkern
2
--------------+----------+---------+-----------------+--------------------
3
select        |  +       |  0      |  ++             | 0
4
--------------+----------+---------+-----------------+--------------------
5
polling mit   |          |         |                 |
6
nonblocking   |  ++      |  --     |  --             | --
7
--------------+----------+---------+-----------------+--------------------
8
2 Threads,    |          |         |                 |
9
beide schrei- |  0       |  +      |  ++             | +
10
ben, Mutex    |          |         |                 |
11
--------------+----------+---------+-----------------+--------------------
12
3 Threads     |  0       |  ++     |  ++             | ++
13
--------------+----------+---------+-----------------+--------------------

von Klaus W. (mfgkw)


Lesenswert?

Stefan schrieb:
> Hab die 2. Nachricht noch gesehen.
>
> Und zwar im readThread arbeite ich mit select(). Wenn etwas reinkommt,
> bricht select() ab und ich kann auf die eingehende Zeile reagieren.
>
> Der sendThread empfängt eine Nachricht (blockierend) und will senden,
> dadurch bricht select im readThread ab, und die Schnittstelle ist frei.
>
> Hab ich das so richtig verstanden?

Keine Ahnung, ich habe deine Beschreibung nicht verstanden.

von Stefan (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Eine Endlosschleife mit nonblocking fd ist aber mit Sicherheit
> die ungeschickteste Lösung.

Bin ich auch deiner Meinung.

Klaus Wachtler schrieb:
> je nachdem, wie sie plaudern.
> Der dritte, der nur versendet, kann doch recht problemlos
> immer erst eine Nachricht komplett schicken, bevor er die
> andere anfasst.

Ist durch select() im readThread() jedoch die Schnittstelle blockiert?
Kann der sendThread() senden, wenn im anderen Thread select() noch 
ausgeführt wird, oder wird, sobald der sendThread() senden will, 
automatisch selct abgebrochen, eben weil sich auf der Schnittstelle was 
tut?

von Sven P. (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> @Sven:
> je nachdem, wie sie plaudern.
> Der dritte, der nur versendet, kann doch recht problemlos
> immer erst eine Nachricht komplett schicken, bevor er die
> andere anfasst.
Ich meinte jetzt die Methodik, mit der ein Thread einen anderen 
anschubst.

von Klaus W. (mfgkw)


Lesenswert?

Sven P. schrieb:
> Ich meinte jetzt die Methodik, mit der ein Thread einen anderen
> anschubst.

Naja, da gibt es ja jetzt mehrere Varianten.

von Klaus W. (mfgkw)


Lesenswert?

Stefan schrieb:
> Ist durch select() im readThread() jedoch die Schnittstelle blockiert?
> Kann der sendThread() senden, wenn im anderen Thread select() noch
> ausgeführt wird, oder wird, sobald der sendThread() senden will,
> automatisch selct abgebrochen, eben weil sich auf der Schnittstelle was
> tut?

select in einem Thread verhindert nicht das Benutzen des fd in einem
anderen.

Im Gegenteil: select() bekommt ohnehin die zu überwachenden fds
in je einem Satz für zu lesende fds, zu beschreibende fds
und fds für out of band data.

In den Satz mit den zu lesenden trägst du deine RS232 ein.
Das ist komplett davon unabhängig, ob du auf demselbem fd
etwas schreibst.
select() springt zurück, wenn von diesem fd etwas ohen zu blockieren
gelesen werden kann.

von Klaus W. (mfgkw)


Lesenswert?

das Ganze übrigens nur für *X, nicht Windows...
Das scheint hier aber gegeben zu sein.

von Stefan (Gast)


Lesenswert?

Danke für all die Hilfe! Nach dem Jahreswechsel habe ich mich wieder an 
den PC gesetzt und es umgesetzt =)

Inotify kann ich leider nicht benutzen, denn dafür wird der 2.6er Kernel 
benötigt und der ist leider nicht kompatibel mit meiner Hardware.

Ich werde dies mit dnotify machen. Leider ist das nicht so mächtig wie 
inotify. Welche Datei sich nun geändert wird nicht mitgeteilt. Das neue 
parsen aller Files stört mich eigentlich nicht, denn a) sind die Files 
klein (100 Bytes), b) wenig vorhanden (< 30 Files) und c) kommt das 
ChangeEvent sehr selten (max. 1x am Tag).

lg Stefan

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.