MultithreadingCodeBild

Vorgestellt: Multithreading mit Arduino

Vor kurzem habe ich eine coole Entdeckung gemacht:
Die Bibliothek mthread, um mehrere Threads auf dem Arduino zu simulieren.

Das hier soll eine kurze Einführung in Multithreading werden.

Einleitung zu Threads:

Um es vorweg zu nehmen: Das Arduino unterstützt kein echtes Multithreading! Es ist also nicht möglich zwei Funktionen gleichzeitig ablaufen zu lassen! Worüber will ich denn dann hier schreiben?
Nun, die Bibliothek mthread ist ein Werkzeug, welches die Schleifen (“loop()”-Funktionen) von unterschiedlichen Threads nacheinander ausführt. Man kann sich nun durchaus fragen, was für ein Mehrwert mthread dann noch hat. Der Große Vorteil ist, dass die Threads unabhängig von einander gestartet, gestoppt und pausiert werden können. Vor allem letzteres ist ein großer Vorteil.

Trotzdem muss das beachtet werden, wenn man plant komplexe Berechnungen “nebenbei” ausführen zu lassen. Das ist trotzdem nicht möglich. Hier sollte man versuchen einzelne Schritte einer komplexen Aktion in mehrere loop() Aufrufe zu gliedern.

Trotz dieser Einschränkung eignet sich mthread großartig, um viele kleine Aktionen zu koordinieren, ohne Massen an wartungsaufwändigem und fehleranfälligem Quellcode schreiben zu müssen

Bibliothek herunterladen:

Die Sourcen kann man sich auf Github runterladen. Ihr braucht die Dateien mthread.h und mthread.cpp. Diese müssen in den gleichen Ordner gespeichert werden, in dem der Sketch liegt. Danach muss Die Arduino IDE neu geladen werden. Jetzt sollten die beiden Dateien oben als Tabs mit auftauchen.

ArduinoIDEDependecies

Einbinden und erstes Programm:

Zuerst muss am Anfang des Sketches die Bibliothek eingebunden werden. Das passiert mit folgender Zeile:

#include "mthread.h"

Nachdem ihr das getan habt, muss die loop() Funktion aus eurem Sketch entfernt werden, denn sie wird schon durch mthread definiert. Eure Programmlogik muss also komplett in den Threads definiert sein.

Der erste Thread:

Eure Threads werden in einer eigenen Klasse definiert. Jeder Thread eine eigene.

Als erstes brauchen wir die Definition der Klasse. Normalerweise wird das in eine Extra Klasse ausgelagert (.h Datei) , ich werde es hier allerdings alles in eine Schreiben. (Bei komplexen Sketches macht es aber durchaus Sinn)

class MainThread : public Thread {
  public:
    MainThread();
  protected:
    bool loop();
};

Für diesen Thread werden zwei Methoden definiert:

  • MainThread: Der Konstruktor der Klasse. Er wird einmalig aufgeführt, denn der Thread/die Klasse erstellt wird
  • loop: Sie ähnelt der loop() Funktion des Standartsketch und wird fortlaufend ausgeführt.

Jetzt muss noch die Logik des Threads definiert werden:

//Definition des Konstruktors
MainThread::MainThread()
{
  //Hier muss in dem Fall nichts unternommen werden.
}

//Definition der Threadschleife
bool MainThread::loop()
{
  // Die if requested:
  if(kill_flag)
    return false;

  Serial.write("Test");

  return true; 
}

Jetzt brauchen wir nur noch den Thread starten. Das wird in der setup() Methode des Thread gemacht:

void setup()
{
  Serial.begin(9600);
  //Thread erstellen und hinzufügen
  main_thread_list->add_thread(new MainThread());
}

 

Das war es! Yay. Der Thread läuft. Es können jetzt weitere Threads hinzugefügt werden.

 

Komplettes Codebeispiel:

#include "mthread.h"

class MainThread : public Thread {
  public:
    MainThread();
  protected:
    bool loop();
};

//Definition des Konstruktors
MainThread::MainThread()
{
}

//Definition der Threadschleife
bool MainThread::loop()
{
  // Die if requested:
  if(kill_flag)
    return false;

  Serial.write("Test");

  this->sleep(1);

  return true; 
}

void setup()
{
  main_thread_list->add_thread(new MainThread());
}

 

Anwendungsbeispiel:

Solange nur ein einziger Thread läuft, haben wir noch kein Vorteil. Deshalb möchte ich kurz ein Beispiel geben, wie die Threads gewinnbringend eingesetzt werden können:

Man kann zum Beispiel die Eingabe und die Ausgabe eines Sketches in jeweilst ein Thread aufteilen. Der Eingabethread läuft ohne Pause, während der Ausgabethread nur jede Sekunde ausgeführt werden soll. So ist eine saubere Trennung möglich, da einzelne Threads unabhängig von einander pausiert werden sollen.

 

Sonderfall: Pausieren:

Bei dem Anhalten des Threads gibt es verschiedene Möglichkeiten, die ich noch erwähnen möchte:

  • delay(1000); Die Standartpausierungsfunktion des Arduinos. Achtung: Wenn Multithreading  verwendet wird, werden damit ALLE Threads angehalten, was meistens unerwünscht ist.
  • this->sleep(1); Hält den aktuellen Thread für die angegebene Zeit in Sekunden an.
  • this->sleep_milli(1000); Hält den aktuellen Thread für die angegebene Zeit in Millisekunden an.
  • this->sleep_micro(1000000); Hält den aktuellen Thread für die angegebene Zeit in Microsekunden an.

 

Links:

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>