Das Konzept der Threads ermöglicht Quasi-Parallelität innerhalb einer Anwendung, und ist unter Betriebssystemen wie OS/2, MacOS oder UNIX schon länger bekannt. Im Gegensatz zum normalen Multitasking können dabei mehrere Threads zu einem Programm (bzw. Prozess) gehören. In MagiC werden Threads ab der Version 4.50 (01.04.96) unterstützt, und sind als Applikation implementiert, d.h. ein Thread besitzt unter MagiC eine eigene Applikations-ID. Das Konzept lehnt sich dabei an das von Sun Solaris 2.x an.
Dieser Abschnitt beschreibt die folgenden Punkte:
Querverweis: GEMDOS Prozessfunktionen Beispiel-Code
Beim Zusammenspiel von Threads und Signalen ist folgendes zu beachten: wird ein Prozess (z.B. per SIGSTOP) angehalten, so werden auch alle zugehörigen Threads angehalten; durch das SIGCONT-Signal können alle Threads wieder aufgeweckt werden. Falls ein Prozess per SIGTERM oder SIGKILL beendet wird, werden auch alle betroffenen Threads automatisch terminiert.
Die Signalbehandlung wird immer nur vom Haupt-Thread übernommen; dies ist derjenige, der mit Pexec gestartet worden ist. Während der Abarbeitung eines Signalhandlers wird deshalb auch nur der Haupt-Thread angehalten, und bei einem Aufruf von Psigreturn in diesen zurückgesprungen.
Achtung: Falls mehr als ein Thread die Signalmaske manipuliert, kann es zu Unstimmigkeiten kommen, falls die Maske nicht in der richtigen Reihenfolge wieder zurückgesetzt wird. Beispiel:
Thread-A rettet alte Maske Thread-A ändert Maske Thread-B rettet alte Maske Thread-A restauriert alte Maske Thread-B restauriert alte Maske
In diesem Fall wird die Signalmaske ungewollt verändert. Eine Lösung dieses Problems besteht darin, jedem Thread eine eigene Signalmaske zuzuteilen, und als effektive Signalmaske die Masken aller Threads durch eine Oder-Verknüpfung zu verbinden. Möglicherweise wird das in einer späteren Version von MagiC tatsächlich der Fall sein.
Querverweis: Threads in MagiC Signale shel_write Prozessfunktionen
Bei der Entwicklung von eigenen Programmen muss unbedingt darauf geachtet werden, daß eine Multithread-sichere Bibliothek verwendet wird. Die Standardbibliotheken von Pure-C etwa sind in weiten Teilen diesbezüglich unbrauchbar. Es muss inbesondere darauf geachtet werden, daß jeder Thread sein eigenes global-Feld erhält. Betroffen sind daher die folgenden Funktionen:
Hinweis: Die original MagiC Dokumentation enthält Beispiele für Multithread fähige AES-Funktionen, an deren Aufbau man sich orientieren kann. Beachtet werden sollte noch, daß der Name eines Threads auf AES-Ebene ungültig ist, d.h. er kann nicht per appl_find oder appl_search gefunden werden.
Querverweis:
Threads und VDI-Aufrufe Prozessfunktionen AES-Bindings
Signale
VDI-Aufrufe sind in den meisten Fällen nicht so kritisch wie AES-Aufrufe, da hier seltener ein Reentranz-Problem auftritt. Der Grund ist darin zu sehen, daß es bei Aufruf von VDI-Funktionen nicht so häufig zu Taskwechseln kommt, wie dies z.B. beim AES der Fall ist.
Problematisch sind VDI-Aufrufe jedoch immer dann, wenn auf Vektorfonts zugegriffen wird, da in diesen Fällen i.d.R. Plattenzugriffe notwendig sind, die in MagiC bekanntlich unterbrechbar sind. In einer solchen Situation kann es also zu einem Task-Switching kommen. Welche VDI-Befehle im einzelnen unterbrechbar sind kann an dieser Stelle nicht beantwortet werden; bei Bedarf ist dies mit den NVDI-Entwicklern abzuklären, so daß dann nur für diese Funktionen reentrante Bibliotheksfunktionen erforderlich wären.
Querverweis:
Threads und AES-Aufrufe Prozessfunktionen VDI-Bindings
Signale
In MagiC laufen Threads auf demselben Prozess, besitzen jedoch eine eigene Applikations-ID, und sind somit eine selbstständige Task. Die folgende Tabelle gibt eine Übersicht über die Resourcen, die ein Thread selbst besitzt, bzw. vom Hauptprogramm benutzt.
Threads besitzen | vom Hauptprogramm wird benutzt |
• Userstack | • Dateihandles |
• Supervisor-Stack | • Basepage |
• Applikations-ID | • Speicherblöcke |
• Resource-Dateien | • aktuelles Laufwerk/Verzeichnisse |
• Menüzeile | • Prozess-ID (PID) |
• Desktop-Hintergrund | • Domain (MiNT bzw. TOS) |
• Fenster | • umask |
• Message Queue | • aktuelle DTA |
• Mauszeiger | • Malloc-Flags |
• VT52-Fenster (optional) | • Kommandozeile und Environment |
• etv_term-Vektor | • Signal-Handler und -maske |
• Semaphoren | • VT52-Fenster (optional) |
Hinweis: Wie man sieht erhält ein Thread damit z.B. eine eigene AP_TERM Nachricht. Bei der Verwendung von Resource-Dateien ist darauf zu achten, daß ggfs. ein eigenes global-Feld benutzt wird.
Zu beachten ist ferner, daß Speicher, den der Thread alloziert, dem Prozess gehört, und bei Beendigung des Threads nicht automatisch freigegeben wird; gleiches gilt für geöffnete Dateien, die erst bei der Programmbeendigung geschlossen werden. Dies muss deshalb ggfs. vom Thread übernommen werden.
Achtung: Es ist unbedingt zu beachten, daß
keine DTA verwendet wird, da die Funktionen Fsfirst, Fsnext,
Fsetdta und Fgetdta nicht Multithread-sicher sind.
Zugriffe auf Dateihandles mit geeigneten Methoden
synchronisiert werden. Es dürfen nicht zwei Threads gleichzeitig auf
dieselbe Datei zugreifen.
Die Funktion Psemaphore ist bereits für Threads ausgelegt, und kann sowohl zur Synchronisation zwischen Prozessen als auch zwischen mehreren Threads eines Prozesses verwendet werden. Beim Beenden eines Threads werden alle von diesem blockierten Semaphoren automatisch freigegeben.
Nach Möglichkeit sollte z.Zt. nur der Haupt-Thread ein Pexec ausführen, nicht jedoch nebenläufige Threads. Theoretisch ist dies jedoch erlaubt, und zwar dann, wenn kein anderer Thread oder der Haupt-Thread Pexec aufgerufen hat, und der Haupt-Thread sich nicht beendet.
Das Problem liegt ganz einfach darin, daß z.Zt. Rücksprungadressen bei Pexec nicht im laufenden Prozess sondern im Parent abgelegt werden, und der Parent des vom Thread gestarteten Prozesses ungültig wird.
Falls ein Thread die Funktion Pterm ausführt, so wird z.Zt. nur dieser Thread beendet. Angemerkt werden soll auch noch, daß ein Thread andere Programme per shel_write (parallel) starten, und auf deren Beendigung warten kann.
Querverweis: Threads und AES-Aufrufe Threads und VDI-Aufrufe Prozessfunktionen GEMDOS
#include <tos.h> #include <mt_aes.h> WORD global[15]; WORD ap_id; WORD fmt_id; LONG cdecl format_thread( struct fmt_parameter *par ) { WORD myglobal[15]; WORD ap_id; /* wir braten das global-Feld der Haupt-APPL nicht über */ ap_id = MT_appl_init(myglobal); (...) } /********************************************************************* * * Startet den Formatier-Thread. * *********************************************************************/ WORD start_format( VOID *param ) { THREADINFO thi; if (fmt_id < 0) /* Thread noch nicht aktiv */ { thi.proc = (VOID *) format_thread; thi.user_stack = NULL; thi.stacksize = 4096L; thi.mode = 0; thi.res1 = 0L; fmt_id = shel_write(SHW_THR_CREATE, 1, 0, (BYTE *) &thi, param); return(fmt_id); } else return(-1); /* Thread läuft noch */ } WORD main( VOID ) { if ((ap_id = MT_appl_init(global)) < 0) Pterm(-1); else { (...) start_format( .... ); while(...) (...); appl_exit(); return(0); } }
Querverweis: Threads GEMDOS Prozessfunktionen Signale