Threads

 

Multithreading

 

Unter Multithreading versteht man die quasi parallele Abarbeitung mehrerer Abläufe in einem Programm. Einer von diesen Abläufen wird Thread (engl. Faden) genannt. Beim Multithreading wird in schnellen Abständen zwischen den verschiedenen Threads gewechselt, so daß beim Anwender der Eindruck von Gleichzeitigkeit entsteht.

 

Die C-Control Pro Firmware unterstützt außer dem Hauptprogramm (Thread "0") bis zu 13 zusätzliche Threads. Mit der Version 2.12 der IDE wurde das Multithreading geändert. Vor 2.12 wurde in den Projekt Optionen jedem Thread eine Anzahl von Bytecodes zugeteilt, nach der der Thread gewechselt wurde. Dies führte of zu einer ungerechten Verteilung, da z.B. Fließkomma Operationen viel mehr CPU Zeit benötigen als andere Bytecodes. Jetzt arbeitet das Multithreading mit Zeitscheiben. Der Anwender kann jedem Thread nun eine Anzahl von 10ms Zeitzyklen zuweisen, nachdem der Threadwechsel stattfindet.

 

Beim Multithreading wird nach einer bestimmten Anzahl von verarbeiteten Byte Instruktionen der aktuelle Thread auf den Status "inaktiv" gesetzt und der nächste ausführbare Thread wird gesucht. Danach startet die Abarbeitung des neuen Threads. Der neue Thread kann wieder derselbe wie vorher sein, je nachdem wie viele Threads aktiviert wurden oder für eine Ausführung bereit sind. Das Hauptprogramm gilt als erster Thread. Daher ist Thread "0" immer aktiv, auch wenn explizit keine Threads gestartet worden sind.

 

 Wird das Hauptprogramm (Thread "0") beendet, stoppen auch alle anderen Threads.

 

Die Priorität eines Threads kann beeinflußt werden, in dem man ändert, wie viele Zeitzyklen ein Thread bis zum nächsten Threadwechsel ausführen darf. Je kleiner die Anzahl der Zyklen bis zum Wechsel, desto geringer die Priorität des Threads.

 

 

Thread Konfiguration

 

Vor IDE Version 2.12 die Konfiguration der Threads wurde in den Projekt Optionen eingestellt. Dies hat sich geändert. Jetzt wird die Konfiguration der threads mit Hilfe des neues "#thread" Befehls im Source Code vorgenommen:

 

#thread thread_nummer, benutztes_ram, anzahl_zeit_zyklen 

 

Ein Thread bekommt für seine lokalen Variablen soviel Platz wie ihm mit #thread zugewiesen wird. Eine Ausnahme ist Thread "0" (das Hauptprogramm). Dieser Thread erhält den restlichen Speicherplatz, den die anderen Threads übrig lassen. Eine RAM Zuweisung mit "#thread 0" für das Hauptprogramm wird daher ignoriert. Man sollte daher vorher planen, wie viel Speicherplatz jeder zusätzliche Thread wirklich benötigt.

 

 Die "#thread" Anweisungen müssen nicht bei den Thread Funktionen sein, sondern dürfen überall im Programm stehen. Benutzt man keine Threads, so ist ein "#thread 0" Befehl unnötig. Vergisst man einen Thread zu definieren, so wird das Thread_Start ignoriert.

 

Beispiel CompactC:

 

#thread 0020   // Hauptthread mit Task Wechsel alle 20 * 10ms =200ms
#thread 112810 // Thread 1 mit 128 byte & Task Wechsel 10*10ms =100ms

#thread 225610 // Thread 2 mit 256 byte & Task Wechsel 10*10ms =100ms

Beispiel BASIC (Syntax identisch zu CompactC):

 

#thread 0020   ' Hauptthread mit Task Wechsel alle 20 * 10ms =200ms
#thread 112810 ' Thread 1 mit 128 byte & Task Wechsel 10*10ms =100ms

#thread 225610 ' Thread 2 mit 256 byte & Task Wechsel 10*10ms =100ms

 

 

Da z.B. Serial_Read wartet, bis ein Zeichen von der seriellen Schnittstelle ankommt, kann es passieren, das der Thread länger als die ihm zugewiesenen Zeitzyklen arbeitet.

 

Beim arbeiten mit Threads sollte man immer Thread_Delay und nicht AbsDelay benutzen. Wird trotzdem z.B. ein AbsDelay(1000) aufgerufen, so wartet der Thread 1000ms, auch wenn ihm weniger Zeit zugewiesen wurde.

 

 

Thread Synchronisation

 

Manchmal ist es nötig, daß ein Thread auf den anderen wartet. Dies kann z.B., eine gemeinsame Hardwareresource sein, die nur ein Thread bearbeiten kann. Oder manchmal definiert man kritische Programmbereiche, die nur ein Thread betreten darf. Diese Funktionen werden durch die Anweisungen Thread_Wait und Thread_Signal realisiert.

 

Ein Thread, der warten soll, führt die Anweisung Thread_Wait mit einer Signal Nummer aus. Der Zustand des Threads wird auf wartend gesetzt. Dies bedeutet, daß dieser Thread bei einem möglichen Threadwechsel übergangen wird. Hat der andere Thread seine kritische Arbeit beendet, gibt er den Befehl Thread_Signal mit der gleichen Signalnummer, die der andere Thread für Thread_Wait benutzt hat. Der Threadzustand des wartenden Threads wechselt dann von wartend zu inaktiv. Jetzt wird er bei einem möglichen Threadwechsel wieder berücksichtigt.

 

 

Deadlocks

 

Begeben sich alle aktiven Threads in einen Wartezustand mit Thread_Wait, so gibt es keinen Thread mehr, der die anderen Threads aus dem wartenden Zustand befreien könnte. Diese Konstellationen sind bei der Programmierung zu vermeiden.

 

 

Tabelle Threadzustände:

 

Zustand

Bedeutung



aktiv

Der Thread wird momentan abgearbeitet

inaktiv

Kann nach einem Threadwechsel wieder aktiviert werden

schlafend

Wird nach einer Anzahl von Ticks wieder auf "inaktiv" gesetzt

wartend

Der Thread wartet auf ein Signal