Nichtblockierende Schleifen sind gut

Hallo allerseits!

Neulich war ich ein bisschen genervt, weil jemand einen Hinweis auf eine „Grundregel“ abgelassen hat, wo ich es unnötig fand.

Das mit den nichtblockierenden Schleifen hat mich aber ein bisschen beschäftigt, denn an der Idee, Schleifen so zu programmieren, dass sie den Programmablauf nicht blockieren, ist was dran. Ich finde, dass da sogar so viel dran ist, dass ich mal etwas Ausführliches dazu lesen sollte. Kennt jemand entsprechende Lektüre?

Ansonsten denke ich gerade darüber nach, ob die „Programmierstrategie“, die dahinter steckt, Grund für eine eigene Programmiersprache wäre. Denn mit dem, was man üblicherweise programmiert (eine Art C/C++), kommt man zwar hin, aber es ist IMO schwer verständlich, wenn man sich nicht ständig mit Programmierkram beschäftigt. Bin ich mit so einer Idee alleine? Gibt es darüber vielleicht Lesestoff?

Eieiei, drei Fragen in einem Posting. Ich hoffe, das ist legal :slight_smile:

Gruß

Gregor

Sprachen gibts schon wie Sand am Meer...
Eine weitere ist vermutlich wenig sinnvoll.
Da muss schon RICHTIG was kommen, damit das keine Totgeburt wird.

Ich möchte dir diese mal empfehlen:
Die Forth Bibel: Starting Forth
Buch Download: Thinking Forth
Eine andere Sicht auf die Dinge..

combie:
Sprachen gibts schon wie Sand am Meer...
Eine weitere ist vermutlich wenig sinnvoll.
Da muss schon RICHTIG was kommen, damit das keine Totgeburt wird.

Ich möchte dir diese mal empfehlen:
Die Forth Bibel: Starting Forth
Buch Download: Thinking Forth
Eine andere Sicht auf die Dinge..

Danke!

Gregor, schon am Lesen ...

Eine Programmiersprache erfinden fine ich nicht sinnvoll. Vor allem wenn man kein Superinformatiker ist.

C hat viele Stärken wenn man mal dahintergekommen ist.

ok, Stringverarbeitung ist ein Schwachpunkt.

Grüße Uwe

uwefed:
ok, Stringverarbeitung ist ein Schwachpunkt.

Selbst ich, als gelernter Pascaler, empfinde Char Arrays inzwischen als gut handelbar. Es gibt jede Menge Funktionen die alles bieten was man braucht.

Ansonsten denke ich gerade darüber nach, ob die „Programmierstrategie", die dahinter steckt, Grund für eine eigene Programmiersprache wäre.

Das ist nicht die Sprache, sondern dein Code.
Bei der PC Entwicklung z.b gibt es es für Multithreading Thread Klassen die du benutzen kannst.

Btw. es gibt auch Multithreading Klassen für die Atmels. (Arduino)

Btw. es gibt auch Multithreading Klassen für die Atmels. (Arduino)

Preemtives?
Nicht wirklich...

Und bei kooperativen Multitasking muss man die Schleifen auch ausrollen können (lernen).

rudirabbit:
Selbst ich, als gelernter Pascaler, empfinde Char Arrays inzwischen als gut handelbar. Es gibt jede Menge Funktionen die alles bieten was man braucht.

Blöd ist halt, dass Char-Arrays nicht der Weisheit letzter Schluss sind. Mit Multibyte-Zeichensätzen kommst Du damit nicht zurecht.

Gruß

Gregor

uwefed:
Eine Programmiersprache erfinden fine ich nicht sinnvoll. Vor allem wenn man kein Superinformatiker ist.
C hat viele Stärken wenn man mal dahintergekommen ist.
ok, Stringverarbeitung ist ein Schwachpunkt.

Wenn es eine Programmiersprache gibt, die nichtblockierende Schleifenkonstrukte ermöglicht, kenne ich sie halt noch nicht. Deshalb zuerst der Gedanke an eine neue Programmiersprache. Vielleicht ist Forth ja das, was ich meine. Ich werde mich jedenfalls mal ein bisschen dort reinlesen.

Gruß

Gregor

Wenn es eine Programmiersprache gibt, die nichtblockierende Schleifenkonstrukte ermöglicht, kenne ich sie halt noch nicht.

Kenne ich auch nicht, und ich habe mich schon mit vielen befasst.

Das ausrollen von Schleifen ist Aufgabe des Programmierers!

Denn eine nicht blockierende Schleife ist keine Schleife mehr.
Es wäre also ein Paradoxon, wenn es sowas geben würde.

Ähnlich absurd, wie der Satz: Ich möchte delay() durch millis() ersetzen.
Das wird nicht gehen, ohne das Programmkonzept über den Haufen zu werfen.

Die einzige Möglichkeit eine nichtblockierende Schleife zu schreiben, ist: Auf die Schleife zu verzichten.
Also: Konzept in die Tonne und neu anfangen.
Egal welche Sprache.

combie:
Kenne ich auch nicht, und ich habe mich schon mit vielen befasst.

Oh, ich dachte, dass Dein Hinweis auf Forth der Hinweis auf eine Sprache sei, die sowas ermöglicht.

combie:
Das ausrollen von Schleifen ist Aufgabe des Programmierers!

Ja, klar. Aber ich finde, dass ein Programm so geschrieben sein sollte, dass man es einem Nichtprogrammierer verständlich machen kann. Und was das angeht, finde ich for-Schleifen wesentlich (also wirklich irre) viel besser, als Konstrukte, die eine Art Scheduler darstellen (darauf läuft das mit den nichtblockierenden Schleifen IMO hinaus).

Gruß

Gregor

Nachtrag: Ich habe gestern (nur mal so zum Spaß) aufgeschrieben, wie ich das verstehe. Gibt es weitere (prinzipiell andere) Varianten?

// Variante 1, wie sie ein *normaler* Mensch kennt
// -----------------------------------------------

loop()
{
  for(byte counter=0; counter<maxVal; counter++)
  {
    // tu was mit counter
  }
}


// Variante 2, die nicht blockiert
// -------------------------------

maxVal=10;
counter=0;

loop()
{
  counter++;
  if(counter<maxVal)
  {
    // tu was mit counter
  }
}

Oh, ich dachte, dass Dein Hinweis auf Forth der Hinweis auf eine Sprache sei, die sowas ermöglicht.

Wenn eine Sprache dieses ermöglicht, dann Forth!

Denn:
In Forth wird die "Sprache" solange verändert, bis sie das Problem optimal abbildet.
In anderen Sprachen muss man das Problem solange modifizieren, bis man es in der Sprache abfassen kann.

gregorss:
Gibt es weitere (prinzipiell andere) Varianten?

Nicht schön, aber zeigt ein anderes Prinzip:

static bool DoINTERVAL = false; // Merker für das Interval Macro

// Start of Interval Macro
#define INTERVAL(interval)                     \
{                                              \
  static unsigned long lastHit = 0;            \
  DoINTERVAL = millis() - lastHit >= interval; \
  if(DoINTERVAL) lastHit = millis();           \
}                                              \
if(DoINTERVAL)
// End of Interval Macro 





void schleife(uint16_t anzahl = 0)
{
  static uint16_t count = 0;
  if(anzahl) count = anzahl;
  if(count)
  { 
    // tue hier, was in der Schleife zu tun ist
    Serial.print("count: ");
    Serial.println(count);
    count--;
  }
}


void setup() 
{
  Serial.begin(9600);
}

void loop() 
{
   schleife();
   
   INTERVAL(1000UL)
   {
     //Tu was, alle 1000ms
     Serial.println("Schleifenstart");
     schleife(5);
   }
}

Genau so, wie INTERVAL in ein Macro gestopft ist könnte man vermutlich die Schleife in ein Macro stopfen.
Aber wie gesagt, nicht schön.
Auch die Verwendung der statischen Variablen ist nicht schön...

PS:
3 Mal "nicht schön"
:wink:

combie:
...
In Forth wird die "Sprache" solange verändert, bis sie das Problem optimal abbildet.
...

Das macht diese Angelegenheit noch interessanter. Deinen „unschönen“ Code gucke ich mir heute Nachmittag einmal genauer an.

Gruß

Gregor

Besonders "unschön" wird es, wenn du es zwei mal verwenden willst

INTERVALL(1000UL) {
   // einmal je Sekunde
}

INTERVALL(1500UL) {
  // alle 1,5 sec einmal ausführen
}

wird so nicht gehen [Nachtrag: Ahem, bitte spätere Posts lesen]

Makros, die ganze Aufrufsequenzen verstecken, können bewundernswert pfiffig sein.
Wenn sie obskure Nebeneffekte haben, die zu unverständlichen Fehlermeldungen oder unerwartetem Verhalten führen, und das Makro nur verwendbar ist, wenn man den Trick dahinter genau kennt, sollte man sowas evtl. eher für sich behalten :wink:

Eine Alternative zu statischen oder globalen Variablen wäre eine Klasse (mit internen Variablen).

class Intervall {
  unsigned long _lastrun;
  unsigned int _period;
public: 
  Intervall (unsigned int period);
  bool trigger(); // returns true once per period
};

Intervall p1(1000);
Intervall p2(1500);

void loop() {

   if (p1.trigger() ) { 
       // einmal je Sekunde
   }
 
  if (p2.trigger() ) {
       // alle 1,5 sec einmal ausführen
   }
}

gregorss:
Oh, ich dachte, dass Dein Hinweis auf Forth der Hinweis auf eine Sprache sei, die sowas ermöglicht.

Worauf Du hinaus möchtest, ist präemptives Multitasking, und das ist keine Sache der verwendeten Programmiersprache, sondern eine Angelegenheit des zugrunde liegenden BETRIEBSSYSTEMS.

Wenn auf der Zielplattform ein BETRIEBSSYSTEM läuft, das PRÄEMPTIVES MULTITASKING unterstützt, dann dürfte mit ziemlicher Sicherheit JEDE PROGRAMMIERSPRACHE für diese Zielplattform ein "Multithreading" unterstützen, also dass Du innerhalb eines Programms mehrere logische Handlungsstränge als "Thread" starten kannst, die per präemptivem Multitasking abgearbeitet werden. Wenn Du dann in einem Thread ein "delay()" machst, wird das BETRIEBSSYSTEM diesen Thread solange deaktivieren, bis die Zeit um ist, und dem Thread erst dann wieder Rechenzeit zuteilen. Und solange sind nur die übrigen Threads aktiv, meistens in einem Zeitscheibenverfahren: Also jeder Thread bekommt zum Beispiel eine Millisekunde Rechenzeit, dann ist der nächste Thread dran etc. Wobei bei Mehrkern-Prozessoren, die Threads vom BETRIEBSSYSTEM auch auf mehrere Rechenkerne verteilt werden können.

Auf Mikrocontrollern ohne Betriebssystem kannst Du Multi-Threading aber komplett vergessen, sondern auf Plattformen komplett ohne Betriebssystem (so wie Arduino) oder auf Singletasking-Betriebssystemen (wie in den Achtzigerjahren MS-DOS) kannst Du - egal mit welcher Programmiersprache - nur Single-Tasking-Programme programmieren, und für ein "kooperatives Multitasking" bist Du selbst verantwortlich, ohne dass Dich dabei ein Betriebssystem durch Zuteilung von Rechenzeit im Zeitscheibenverfahren mit einem präemptiven Multitasking unterstützt.

Multitasking-Betriebssysteme sind in zahlreicher Ausführung vorhanden: Windows, Linux, MAC OS, Android etc., und für jedes Betriebssystem existiert mindestens eine, meistens aber sogar mehrere verschiedene Programmiersprachen, die Multithreading unterstützen, da braucht gar nix neu erfunden zu werden, weil es das alles schon gibt.

Aber es gibt eben kein Multi-Threading auf Plattformen, die ohne Betriebssystem laufen.

Hallo Michael, hallo Jurs,

danke für Euren Senf zur Sache! Das sind auf jeden Fall Trigger, die mein Hirn die nächsten Tage ein bisschen mehr rotieren lassen werden.

Gruß

Gregor

michael_x:
Eine Alternative zu statischen oder globalen Variablen wäre eine Klasse (mit internen Variablen).

class Intervall {

[..]
};

Hallo zusammen,
genau das ist die Lösung, die ich auch verwendet habe.
Ich selbst habe sie etwas anders implementiert, da ich noch andere für mich wichtige Dinge hinein implementiert habe (zb.: Synchronisation in dem Sinn, dass mehrere Intervall-Objekte ihr Intervall untereinander (de)synchronisieren, sonst häufen sich die Aufrufe um einen Zeitpunkt herum, ..)
Das gleiche könnte man in der gleichen Klasse mit Events machen, indem man statt Zeitpunkten (= millis() auswerten) boolsche Funktionen als Parameter übergibt, die in der Klasse ausgewertet werden also (vorsicht Pseudocode !) :

bool teste()
{
  if (x > y) 
       return true;
  else return false
}

if (p1.trigger(@teste) ) 
{ 
  tuwas();
}

Im Endeffekt (und so sieht es bei mir dann fast aus hat man dann einen loop() der aus solchen triggerausgelösten
Sequenzen besteht und sich das Multitasking auf Zeitscheiben oder Eventbasis selbst implementiert.

Grüße Harry

gregorss:
Mit Multibyte-Zeichensätzen kommst Du damit nicht zurecht.

Auf dem Arduino nicht. Aber in C9x oder C++ gibt es auch Multi-Byte char Datentypen und die entsprechenden String Funktionen dafür.

Jeder Datenaustausch jenseits einzelner Bytes ist nicht-trivial.
wchar_t gibt es zwar theoretisch in avr-gcc, ich würde nicht empfehlen, dies zu verwenden.

Selbst Texte aus einzelnen Bytes sind problematisch, bei allem ausserhalb 7bit-ascii.
Muss eben die Codierung übereinstimmen, damit es geht.

Der größte Engpass wird ein LCD Display sein. Hat wohl keines einen Zeichensatz, der mit dem SerialMonitor 100% übereinstimmt, und kann schon gar kein UTF-8.
Wobei da erstmal rauszukriegen wäre, welchen Zeichensatz SerialMonitor und der Compiler (auf welcher Plattform) verwenden.

Ist aber hier komplett OT, genauso wie ein Arduino Forth Interpreter :wink:
Sollten froh sein, keine BASCOM Fans hier zu haben

michael_x:
Besonders "unschön" wird es, wenn du es zwei mal verwenden willst

INTERVALL(1000UL) {

// einmal je Sekunde
}

INTERVALL(1500UL) {
  // alle 1,5 sec einmal ausführen
}



<sup>wird so nicht gehen</sup>

Natürlich geht das!
Du kannst mit dem Macro soviele INTERVAL Abschnitte realisieren wie du möchtst!

Tipp:
Erst testen, dann mäckern.
:wink:

Worauf Du hinaus möchtest, ist präemptives Multitasking, und das ist keine Sache der verwendeten Programmiersprache, sondern eine Angelegenheit des zugrunde liegenden BETRIEBSSYSTEMS.

Forth unterscheidet nicht zwischen Betriebsystem und Sprache.
Preemtives Multitasking ist da mit wenigen schlichten Forth Worten realisierbar.

Ist aber hier komplett OT, genauso wie ein Arduino Forth Interpreter

Die Frage zielte auf "andere Sprache als C/C++".
OT ist also was anderes.
Es gibt sogar recht brauchbare Forth Systeme für AVRs.
Wobei Forth sich auf von Neumann Architekturen wohler fühlt.

Arduino Forth Interpreter

Interpreter?
Eine gewagte These!
Ich spreche lieber von einem Forth System.
Das triffs eher.
Alleine schon weil ein Forth System meist mehrere Kompiler, Interpreter, Assembler beinhaltet.