Forum: PC-Programmierung Probleme mit "Asyncroner" Verarbeitung bei C


von Hans (Gast)


Lesenswert?

Hallo,

auf Anragen von Jim Meba hier nochmal ein "extra post"

ich habe eine werteliste welche abgearbeitet werden muss... die ganze
liste hat einen fest vorgegebenen timer...

also z.B. port1: [ 0, 1, 1, 0, 1 ] mit 300ms dann muss ich alle 300ms
parameter für parameter abarbeiten...

allerdings soll nebenbei das "normale" program weiterarbeiten. Ebenso
kann es sein, dass nebenbei mehrere parameter laufen also port2: [ 0, 1
] 10000ms, hier ist die parametergruppe post1 evtl noch nicht fertig
aber es startet parametergruppe 2

da ich verschiedene "unterprogramme" habe die parallel laufen sollen
habe ich mir das in einen vorherigen foreneintrag abgeguckt...

ich habe also verschiedene counter welche zählen und sobald einer auf
seinen wert ist, wird der befehl ausgefürt und der wert zurückgesetzt:
1
int cnt[4];
2
int prm[4] = { 20, 5, 3 16 };
3
4
while(1) {
5
   for( int i = 0; i < 4; i++ ) {
6
      cnt[i]++;
7
   }
8
9
   if( cnt[0] < prm[0] ) {
10
      cnt[0] = 0;
11
      //aufgabe 1 ausführen
12
   }
13
14
   //selbiges gilt auch für aufgabe 2...
15
16
   for( t = 0; t < (sizeof( cnt ) / 4); t++ ) {
17
      cnt[t]++;
18
   }
19
20
   int tSleep = speed * 5 * 1000; // ca 5ms
21
   usleep( tSleep ); 
22
}
23
24
return 0;

ich weiß, dass ich hier keine genauen zeiten hinbekomme... das
interessiert mich aber auch garnicht... ich habe für die Hardware ein
paar Zeiten einzuhalten (lesen von adc schreiben von gpio) und diese
werde ich "großzügig" einhalten... ob da jetzt 50ms mehr oder weniger
sind ist mir egal


ich weiß jetzt noch nicht exakt wie (da bin ich noch bei der findung)
aber irgendwie werd ich jetzt von außen befehle Reinbringen (vermutlich
habe ich eine SQLite Datenbank oder ein File) und da wird dann eine 
schleife oder [das
bekomm ich noch nicht so hin], die einzelnen Zustände eingebracht.
=> es ist allerdings so, dass von außen der befehl zum Startzeitpunkt 
(der abarbeitung), komplett vorliegt, aber er dann aber nicht komplett 
gleich sondern mit einer zeitlichen abfolge (delay / timeout) 
abgearbeitet werden soll.

hat jemand einen tipp für mich?

kurzum... mir liegt ein array vor welches abgearbeitet werden soll...

ich habe sogar bereits eine "forschleife" welche alle schaltzustände
durchläuft... allerdings durchläuft die forschleife innerhalb von ms
(oder noch schneller) und ich brauche aber die vordefinierte zeit...
einen SLEEP kann ich nicht einbauen, weil sonst das programm steht :(
1
for( j = 0; j < (sizeof( cmd ) / 4); j++ ) {
2
   printf( cmd[j] );
3
// HIER BRAUCH ICH DIE PAUSE, ABER SO, DASS MEIN HAUPTSCRIPT WEITERLÄUFT
4
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

C kennt keine Nebenläufigkeit. Entweder es gelingt Dir, Deine 
verschiedenen Aufgaben in kleine Schnipselchen zu zerlegen, die Du 
zeitlich gesteuert wechselweise aufrufst, oder Du musst Dir ansehen, 
unter welchem Betriebssystem Dein C-Programm läuft, und ob das 
Betriebssystem Dir Funktionen für Nebenläufigkeit zur Verfügung 
stellt.

Worauf soll denn das Programm laufen? Auf einem PC mit Windows, Linux 
o.ä., oder auf einem Microcontroller ohne Betriebssystem?

von Mikro 7. (mikro77)


Lesenswert?

Möglichkeiten...

...für jede Aufgabe einen separaten Thread starten

...für jede/alle Aufgabe(n) läuft ein asynchroner Timer der mit dem 
ersten auszuführenden Zeitpunkt geladen wird und nach dem Eintreten mit 
dem nächsten Zeitpunkt usw. Als Timer bieten sich Peripherie + Interrupt 
an oder UNIX Timer und Signale.

...wenn dein Hauptprogramm Ereignisgesteuert ist und in kleine 
(zeitliche) Tasks zerlegt werden kann baust du dir einen Mini Scheduler 
mit Timer und Task Queue wo Timer Events bspw. bevorzugt werden.

... wenn das alles nicht geht dann kannst du vielleicht in deinem 
Hauptprogramm "Synchronisationspunkte" auswählen an denen du prüfst ob 
eine Aufgabe vorliegt (polling).

von Achim (Gast)


Lesenswert?

Oder die sogenannte SPS-loop.
Falls ohne rtos, dann mit den wichtigsten Tasks im Interrupt. Wichtig 
scheinen bei dir robuste brotlose Timer. Da scheint es hier im Forum 
abenteuerliche Auffassungen zu geben.

von Hans (Gast)


Lesenswert?

ou sorry,

ich habe vergessen dazu zu schreiben, dass ich hier auf einen rPi3 
arbeite mit neusten Linux.

meine aktuelle Idee ist es sozusagen immer ein "neues" Programm 
aufzurufen, also ein 2. c schreiben, dass das ganze dann abarbeitet... 
hat obendrein den großen vorteil, wenn das "Hauptprogramm" abstürzt 
werden die schritte sauber "Fertig" abgearbeitet.

Leider weiß ich noch nicht genau wie ich das mache. <- bin da auf den 
Beta Beginner Level :) ...

aber Interessant wäre ob es ggf. andere "Anfänger-Möglichkeiten" gibt...

gleichzeitig weiß ich auch nicht wie genau ich verhinder, dass 
gleichzeitig gelesen und geschrieben wird
=> also wenn ich jetzt meinen 2. Thread starte, der z.B. alle Sekunden 
nen Port an und ausschaltet darf "genau" in dieser Zeit (beim schalten) 
<- (zeitraum von n Millisekunden) der selbe Port nicht gelesen werden...
=> meine Idee war es also ein fifo file anzulegen in dem ein "HALT" 
steht und sozusagen der "Reader" für n Millisekunden ausgebremst wird...

Was haltet ihr von meinen "Gedankengängen"? Ist das zu Kompliziert oder 
zu Einfach gedacht?

Program 1:
dauerschleife {

  lese informationen von Programm 2, falls halt da warte für n ms

  Informationen lesen

  befehle an Programm 2 Senden

}

Programm 2

Befehlsinput

Arbeite einzelne schritte mit nötigen Delay ab
vor jedem "Schaltvorgang" sende Informationen an Program 1 mit PAUSE

von codix (Gast)


Lesenswert?

Hans schrieb:
> meine aktuelle Idee ist es sozusagen immer ein "neues" Programm
> aufzurufen, also ein 2. c schreiben, dass das ganze dann abarbeitet...

Schau Dir einmal fork() an und lese Dich in named Pipes ein.
Dann sollte der Groschen fallen.
So einfach wie du Dir das denkst funktioniert das nicht bzw. nicht 
stabil.
Vergiss ganz schnell den sleep(), der bringt Dich hier nicht weiter.
Einen Prozess schlafen zu lassen ist oft der falsche Weg.

von SchneeflöckchenInDerGlaskugel (Gast)


Lesenswert?

Ich möchte Dir raten zunächst einmal einige vorbereitende Schritte zu 
machen damit Du gedanklich etwas klarer siehst, welche Aspekte dieses 
Problem hat. Damit zerlegst Du es in kleinere Einheiten und kannst Dich 
Schritt für Schritt vorarbeiten. Es ist wesentlich umfangreicher, als 
Du, nachdem wie und was Du schreibst, vermutest.

Bitte fasse das nicht so auf, dass ich Dich für dumm halte oder so. Es 
ist gute alte Methodik, Probleme von unten her anzugehen, anstatt alle 
auf einmal. Das haben auch die Überflieger so gemacht. Jedenfalls viele.

Es wäre aus meiner Sicht sogar ratsam dabei zunächst auf einen einfachen 
Mikrocontroller wie den AVR zurückzugehen, denn die Verwendung eines 
ausgewachsenen Betriebssystems würde erfordern, sich allen Problemen 
gleichzeitig zu stellen. Das ist aber bis zu einem gewissen Grad 
Ansichtssache. Dennoch scheint mir dieser Rat nützlich zu sein.


Man müsste vielleicht erstmal ein paar Punkte klären, damit man einen 
genauer auf Dich zugeschnittenen Rat geben kann.

1. Ich vermute mal, dass Du mit einem rPi zunächst mal ein paar LEDs 
hast leuchten lassen. Ist das so?
2. Was ich nicht ohne weiteres vermutet hätte, ist, dass Du so ohne 
weiteres an die Timer herankommst. Eher noch an irgendwelche abstrakten 
Funktionen, die etwa Callbacks aufrufen, wenn gewisse Zeiten abgelaufen 
sind. Ist das so?
3. Problemstellungen wie "Dining Philosophers" sagen Dir wahrscheinlich 
nichts, oder?
4. State-Machines sagen Dir vermutlich nichts, oder?
5. Read-Modify-Write sagt Dir vermutlich nichts, oder?
6. Locks sagen Dir vermutlich nichts, oder?
7. Was Treiber von Anwendungen (im Kontext von Linux) unterscheidet, 
weißt Du?
8. Was grundsätzlich Echtzeitanwendungen von üblichen Anwendungen 
unterscheidet, weißt Du?
9. Hast Du mal was von Minix oder einem Herrn Tanenbaum gehört?

Was hast Du bisher gemacht? Was für Projekte realisiert? Welchen Code 
hast Du selbst geschrieben? Auf welchen Plattformen? Wie lange? Was hast 
Du gelesen? Was studiert? Was arbeitest Du?


Meine Vorgehensweise ist eher methodisch als mittendrein zu springen. 
Muss man nicht so machen. Aber das ist mein ehrlich gemeinter Rat.

von SchneeflöckchenInDerGlaskugel (Gast)


Lesenswert?

@ Hans

Naja. Vielleicht war meine vorherige Antwort unnötig elaboriert.


Wenn ich mal von Deiner Eröffnungspost ausgehe, versuche ich Deinen 
Algorithmus mal neu zu formulieren.


A) Die periodische Verarbeitung von Listenwerten

1. Du hast Listen von Werten.
2. Die Anzahl der Listen steht fest, wenn Du das Programm schreibst und 
kompilierst.
3. Die Inhalte der Listen stehen fest, wenn Du das Programms schreibst 
und kompilierst.
4. Jeder Liste ist jeweils eine Zeit zugeordnet.
5. Diese Zeit gibt an, in welchem zeitlichen Abstand jeweils ein Wert 
aus dieser Liste verarbeitet werden soll.

Das geht, in dem Du einen Timer nimmst, der periodisch einen Interrupt 
auslöst. Der Zeitabstand ist so gewählt, dass die Timer-Periode ein 
Vielfaches aller denkbaren Zeiten der jeweiligen Liste ist. (Falls das 
nicht so geht, müsstest Du das mal erklären, warum). In dem Interrupt 
zählst Du, je Liste eine Zeitvariable herunter. Das muss so sein, weil 
die Zeiten unterschiedlich sein können. (Es gäbe da noch andere 
Möglichkeiten, aber das ist die einfachste). Ist für eine Liste die Zeit 
abgelaufen, dann setzt Du deren Ablaufzeit wieder auf den Anfangswert, 
nimmst das nächste Element aus der Liste und verarbeitest es und zählst 
einen Index auf das aktuelle Element hoch. Das machst Du für alle 
Listen.
Es gibt für die Funktion, die ein Listenelement verarbeitet zwei 
Möglichkeiten, von denen ich eine nenne. Du kannst je Funktion ein Flag 
vorsehen, dass Du im Interrupt setzt und in einer endlosen Schleife im 
Hauptprogramm immer wieder abfragst und die Funktion aufrufst, falls das 
Flag gesetzt ist. Danach setzt Du das Flag wieder zurück. Die Funktion 
darf niemals länger dauern als die Periode der jeweiligen Liste. 
(Tatsächlich muss sie sogar noch schneller sein, denn die Funktionen der 
anderen Listen und Dein Hauptprogramm brauchen ja auch Zeit)-


B) Das "normale" Programm

Da kann ich Deinem Text nicht viele Einzelheiten entnehmen.

1. Das "normale" Programm bekommt Befehle.
2. Diese Befehle sind mehrgliedrig in Bezug auf das was sie auslösen. 
Oder anders ausgedrückt: Sie lösen mehrere Funktionen aus.
3. Diese Funktionen sollen parallel zu den Listen-Timer-Funktion 
abgearbeitet werden.
4. Die Befehle bzw. Befehlsteile werden fallweise mit einer gewissen 
Verzögerung abgearbeitet.

Dazu hat Dir Rufus schon einmal einen groben Anhaltspunkt gegeben. 
Nämlich die nötigen Funktion zu untergliedern.
Daraus, dass die Befehle ohnehin schon mehrere Funktionen "meinen" 
ergibt sich eine der Gliederungen.
Je nachdem wie lange die Funktionen jeweils dauern, kann es nötig sein, 
sie weiter in kleinere Funktionen zu zerlegen.
Deren Reihenfolge kannst Du über State-Machines (im einfachsten Fall ein 
Zähler) steuern. Wichtig ist, das selbst im ungünstigsten Fall jede 
dieser Teilfunktionen höchstens so lange dauert, das alle 
Listen-Timer-Funktionen noch ausgeführt werden können.
Dazu sind ein paar Überlegungen nötig, für die Du folgendes vorher 
feststellen muss. Wie lange können die Funktionen jeweils maximal 
dauern? In welchen Zeitabständen werden sie minimal aufgerufen. Was ist 
die Summe der Ausführungszeiten aller dieser Listen-Timer-Funktionen 
maximal?
Was die verzögerte Ausführung betrifft, lässt sich die in das Schema der 
Listen-Timer-Funktionen einordnen. Es ist ja egal, ob die Funktion auf 
eine Liste zugreift oder nicht. Wesentlich ist nur, dass sie nach einer 
gewissen Zeit anfängt zu arbeiten. Die möglichen minimalen Zeitabstände 
bzw. die Auflösung der Zeitangaben musst Du dann bei der Periode des 
Timers berücksichtigen.

C. Resourcen - verhindern des gleichzeitigen Setzens und Lesens

1. OK. Das ist soweit einfach. Man darf nicht gleichzeitig lesen und 
schreiben.

Das Problem hast Du, wenn Du der obigen Beschreibung folgst sozusagen 
nebenbei erledigt, in dem Du diese Funktionen, die auf die Resourcen 
(die Ports) zugreifen, nicht im Interrupt sondern aufgrund des Flags im 
Hauptprogramm abarbeitest.
Das gilt aber nur, falls diese Funktionen nicht unterteilt wurden, weil 
sie sonst zeitlich zu lang geworden wären.
Dann musst Du ein Flag verwenden, ein Lock. Das wird zunächst geprüft ob 
es nicht schon gesetzt ist. Falls nicht, wird es gesetzt und die 
Resource kann verwendet werden. Fall es doch gesetzt ist, macht die 
Funktion erstmal garnichts.
Ein kniffliges Detail dabei ist, ob die Timer-Listen-Funktionen (falls 
sie denn auf diese gemeinsamen Resourcen überhaupt zugreifen) eigentlich 
zwingend so und so viel mal während eines Zeitraum, wenn auch mit 
möglichen Verzögerungen aufgerufen werden sollen oder ob es egal ist, 
wenn eine Funktion mal nicht aufgerufen wird. Allerdings ergibt sich das 
erst aus den Überlegungen über die Zeitdauer der Funktionen (und die 
wird sich real erst aus verschiedenen Zeitnahmen der Einzelfunktionen) 
ergeben.
Auf die beschriebene Weise aber umgehst Du das Problem, das faktisch 
nebenläufige Prozesse auftreten, die auf das Lock-Flag zugreifen. Da das 
auf die beschriebene Weise nur im Hauptprogramm geschieht, kann nichts 
passieren. Falls das so für Dich nicht geht, müsste man das mal einzeln 
besprechen.

OK. Langer Sermon.
Du siehst jedenfalls, dass das Alles nicht wirklich trivial ist und das 
einem das nicht so in den Schoß fällt. Das musste ich auch erst alles 
lesen und ausprobieren.

von SchneeflöckchenInDerGlaskugel (Gast)


Lesenswert?

Mein Auge fällt da gerade noch einmal auf folgende Sätze von Dir.

> Leider weiß ich noch nicht genau wie ich das mache. <- bin
> da auf den Beta Beginner Level :) ...

> aber Interessant wäre ob es ggf. andere "Anfänger-Möglichkeiten" gibt...

Ich bezweifle, dass es so eine Anfängermöglichkeit gibt. Das von mir 
beschriebene ist allerdings durch bestimmte Spezifikationen (die ich 
oben auch genannt habe) in der Weise beschränkt, dass es recht einfach 
zu verstehen und zu realisieren sein müsste.

Es gibt vielleicht eineb Satz von Funktionen und Datenstrukturen (ein 
Framework - eine Library), welche die von mir beschriebene 
Vorgehensweise in der Schreibweise vereinfacht. Aber sowas ist mir nicht 
bekannt.
Es scheint mir auch fraglich ob sowas veröffentlicht würde, denn im 
Detail gibt es dann meist soviele Unterschiede in den Anforderungen, 
dass es entweder garnicht passt oder dass der Overhead bei der 
Ausführung oder die Einarbeitung nicht lohnen würde.
Aber ganz und gar ausschliessen würde ich das auch nicht.

Das Ganze ist aber de-fakto nur das Ergebnis vieler trivialer 
Einzelüberlegungen.
Wenn Du Dir Zeit nimmst, das mal nachzuvollziehen bzw. vielleicht meinem 
obigen Rat folgst und die einzelnen Aspekte selbst erforschst, wirst Du 
hinterher denken: Das ist Alles? Ist ja simpel!
Ist es auch, aber die Wirkung ist enorm, weil man sich das als Anfänger 
sehr kompliziert denkt und garnicht weiß wo anfangen! Denk nur: Viele 
einzelne Funktionen werden periodisch, mit unterschiedlichen Perioden 
und zusammen mit befohlenen Funktionen ausgeführt und alles klappt wie 
am Schnürchen. Also ich finde das bemerkenswert.

Naja. Hoffe Dir ist ein wenig geholfen.

von SchneeflöckchenInDerGlaskugel (Gast)


Lesenswert?

@ Hans
Da ist mir doch leider ein Fehler unterlaufen. Sorry.

Es hiess oben:
"Das geht, in dem Du einen Timer nimmst, der periodisch einen Interrupt
auslöst. Der Zeitabstand ist so gewählt, dass die Timer-Periode ein
Vielfaches aller denkbaren Zeiten der jeweiligen Liste ist."

Das ist falsch.

Die Timerperiode muss so gewählt werden, dass alle möglichen Perioden 
der Funktionen als ganzzahliges Vielfaches dieser Periode ausgedrückt 
werden können.

Z.B.
Funktion 1 soll in 30ms Periode abgearbeitet werden.
Funktion 2 in 25ms Periode.

Ein gemeinsamer Teiler ist 5ms. Also Funktion 1 jeweils nach 6 und 
Funktion 2 jeweils nach 5 Perioden von 5ms.


OK. Jetzt mache ich aber mit meinem eigenen Kram weiter.

von Mark B. (markbrandis)


Lesenswert?

Hans schrieb:
> gleichzeitig weiß ich auch nicht wie genau ich verhinder, dass
> gleichzeitig gelesen und geschrieben wird
> => also wenn ich jetzt meinen 2. Thread starte, der z.B. alle Sekunden
> nen Port an und ausschaltet darf "genau" in dieser Zeit (beim schalten)
> <- (zeitraum von n Millisekunden) der selbe Port nicht gelesen werden...
> => meine Idee war es also ein fifo file anzulegen in dem ein "HALT"
> steht und sozusagen der "Reader" für n Millisekunden ausgebremst wird...
>
> Was haltet ihr von meinen "Gedankengängen"? Ist das zu Kompliziert oder
> zu Einfach gedacht?

Die Lösungen, die Du suchst, liegen üblicherweise auf der Ebene des 
Betriebssystems. Stichworte:
-Threads
-Interprozesskommunikation
-Semaphoren bzw. Mutual Exclusion

von Rolf M. (rmagnus)


Lesenswert?

Rufus Τ. F. schrieb:
> C kennt keine Nebenläufigkeit.

Doch, seit C11 schon, allerdings optional. Siehe 
http://en.cppreference.com/w/c/thread

von fürn Hugo (Gast)


Lesenswert?


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.