DOS 2.0: Hintergrundprzesse als TSR-Prgramme : Terminate and Stay Resident (d.h.: keine Speicher-Freigabe) ermöglichte Verfügbarkeit/ Reaktivierbarkeit (meist über Htkey ) z.b.: Verwaltung / Versrgung vn Druckaufträgen (Screensht, Drucker-Spler); nicht-englische Tastaturtreiber 16-Bit-Windws(1985): Nicht-präemptives Multitasking: Peripherie (Tastatur, Maus) knnte Nachricht (Aufruf) auslösen, die inaktives Prgramm im Speicher aktivierte. Prgramm mußte für Kntrll-Abgabe knzipiert sein ( kperatives Multitasking), snst Blckierung möglich. OS/2 (MS/IBM, 1988): Präemptives Multitasking: Sw ( Presentatin Manager ) rdnete Ereignisse den laufenden Prg. zu. Warten auf (serielle) Ereignis-Zustellg wirkte entgegen. z.b.: lange Text-Sicherung verzögert Fkus-Wechsel
32-Bit-Windws (1995): Multithreading: Aufteilung der Anweisungsflgen eines Prgramms in mehrere Ausführungsstränge, die parallel (präemptiv) ausgeführt werden. Der Hauptthread (primary) kann weitere (secndary) Threads erzeugen und diese wiederum andere. Alle Threads erhalten (verdrängend) Kntrlle/CPU-Zeit wie separate Prgramme bekmmen eine eigene Nachrichtenwarteschlange zugewiesen gehören zum selben Przeß (teilen Speicher und static-variablen) haben eigene Stacks (lkale Var. und Przessr-/Cprz.-Zustand) Wichtige Kriterien bei der Einrichtung vn Threads: Hauptthread f. Erzeugung v. Fenstern u. Verteilung v. Nachrichten, alle anderen zur Erledigung vn Aufgaben im Hintergrund. z.b.: Windws-Aufrufe jederzeit bedienen 1/10-sec-Regel : was länger braucht, gehört in den Hintergrund z.b.: Menü-/Prgramm-Nutzung während langwieriger Speicherung Thread-Abspaltung auf Sinnfälligkeit überprüfen z.b.: Bildbearbeitung während Bildladen fragwürdig
Anlegen und Starten des Threads ThreadPrc(): #include <windws.h> //genauer: winbase.h Thread- Kennung HANDLE CreateThread (LPSECURITY_ATTRIBUTES lpthrattr, DWORD dwstacksize, DWORD WINAPI ThreadPrc, LPVOID lpparam, DWORD dwflags, LPDWORD lpthreadid); Sfrtiges Beenden eines Threads (bei return entbehrlich): VOID ExitThread (DWORD dwexitcde);//beendet ThreadPrc BOOL ClseHandle(HANDLE handle); //Resrc.freigabe(@TRUE) Parameter: lpthrattr: dwstacksize: ThreadPrc: lpparam: dwflags: lpthreadid: dwexitcde: Zeiger auf interne Struktur (betr.interprz.-kmmun.): NULL Startwert Stackgröße (0: Standard autm. Anpassung) Adresse DWORD WINAPI ThreadPrc (PVOID *lpparam) (32-Bit-)Argument vn ThreadPrc() 0. CREATE_SUSPENDED: wartet bis ResumeThread-Aufruf Zeiger auf 32-Bit-Var., die Thread-Kennung erhält (. NULL) Exit-Cde (individuelle 32-Bit-Variable z.b. lpparam)
Thread-Kennung (Handle) Empfhlene Funktin zum Anlegen und Starten vn ThreadPrc(): #include <prcess.h> unsigned lng _beginthread( vid( cdecl *ThreadPrc)(vid *), unsigned uistacksize, vid *pparam); Sfrtiges Beenden und Ressurcen-Freigabe eines Threads: vid _endthread (vid); //bei return entbehrlich Parameter: ThreadPrc: uistacksize: pparam: Startadresse vn vid ThreadPrc(vid *pparam) [ genauer: vid cdecl ThreadPrc (vid *pparam); cdecl: default calling cnventin fr C / C++ prgrams ] anfängliche Stackgröße; 0: Standardwert (1 MB) bei Bedarf autmat. Anpassung (32-Bit-)ThreadPrc()-Argument; meist: Struktur-Zeiger Einzige Möglichkeit zur Thread-Individualisierung!
Thread-Synchrnisatin (I) Blckierung des generierenden (primären) Threads, damit seine Beendigung nicht vrzeitig die sekundären Threads beendet: #include <windws.h> //genauer: winbase.h DWORD WaitFrMultipleObjects (DWORD ncunt, CONST HANDLE *handle, BOOL WaitAll, DWORD msec); Parameter: ncunt: Anzahl zu synchrnisierender (sekundärer) Threads handle: Zeiger auf Feld mit (Thread-. anderen Objekt-)Handles WaitAll: Typ des Wartens (TRUE: warten auf alle Threads; FALSE: warten, bis irgendein Thread fertig ist) msec: max. Wartezeit in Millisekunden (INFINITE: beliebig) Rückgabewert: Grund zur Beendigung des Wartens (Kennzahl)
Beispiel: Multi-Timer typedef struct { time_t start, tbell[num]; timerdat; //#define NUM 5 vid bell(vid *idx) {static timerdat *tim=null; time_t tj=0,ttick=0;//int j1=0; if (!tim) tim = init(); d { time(&tj); if (tj>=ttick){ system(cls); /*(...)*/ fr (j1=0; j1<num; j1++) /*..printf()..*/; ttick++; while(tj<tim->tbell[(int)idx]); printf("\a"); // beep return; timerdat *init(vid) //Speicher: {static timerdat tim; tim.tbell[0]=5; tim.tbell[1]=10; tim.tbell[2]=15; tim.tbell[3]=8; tim.tbell[4]=7; time(&tim.start); return(&tim); vid multibell(vid) {int j1=0; int Zeiger knvertierbar fr(j1=0;j1<num;j1++) bell((vid*)j1); return; int main (vid) { multibell(); system(cls); return 0; (MultiBell.exe)
Beispiel: Multi-Timer (Thread-Versin) typedef struct { time_t start, tbell[num]; timerdat; //#define NUM 5 vid bell(vid *idx) {static timerdat *tim=null; time_t tj=0,ttick=0;//int j1=0; if (!tim) tim = init(); d { time(&tj); if (tj>=ttick){ system(cls); /*(...)*/ fr (j1=0; j1<num; j1++) /*..printf()..*/; ttick++; while(tj<tim->tbell[(int)idx]); printf("\a"); // beep _endthread(); return; timerdat *init(vid) //Speicher: {static timerdat tim; tim.tbell[0]=5; tim.tbell[1]=10; tim.tbell[2]=15; tim.tbell[3]=8; tim.tbell[4]=7; time(&tim.start); return(&tim); vid multibell(vid) {int j1=0; unsigned lng hthread[num]; fr(j1=0;j1<num;j1++)hthread[j1]= _beginthread(bell,0,(vid*)j1); WaitFrMultipleObjects(NUM, (CONST HANDLE)hThread, TRUE, INFINITE); return; int main (vid) { multibell(); system(cls); return 0; (MultiBellThread.exe)
Thread-Synchrnisatin (II) Einrichtung kritischer Abschnitte zur Verhinderung v. Kllisinen bei Inanspruchnahme exklusiv nutzbarer Ressurcen; Implementierung als Instanz einer (Windws-internen) Struktur LPCRITICAL_SECTION, die nur durch 4 Systemfunktinen manipuliert werden darf: #include <windws.h> //genauer: winbase.h Vereinbarung und Initialisierung der benötigten Struktur für den Schutz einer Ressurce: CRITICAL_SECTION cs; VOID InitializeCriticalSectin(LPCRITICAL_SECTION *cs); Eintritt in den bzw. Austritt aus dem kritischen Abschnitt: VOID EnterCriticalSectin (LPCRITICAL_SECTION *cs); VOID LeaveCriticalSectin (LPCRITICAL_SECTION *cs); zeitkritisch! Freigabe der Ressurcen eines kritischen Abschnitts: VOID DeleteCriticalSectin (LPCRITICAL_SECTION *cs);
Beispiel: Multi-Timer (Thread/CS-Versin) typedef struct {/*...*/ CRITICAL_SECTION cs4pr; timerdat; Thread-Identifikatin! vid bell(vid *idx) { static timerdat *tim=null; if (!tim) tim = init(); /* (...) */ EnterCriticalSectin (&tim->cs4pr); /*system(cls); printf(...)*/ LeaveCriticalSectin (&tim->cs4pr); /* (..._endthread();...) */ return; vid multibell(vid) { int j1=0; unsigned lng hthread[num]; static timerdat *tim=null; if (!tim) tim = init(); InitializeCriticalSectin (&tim->cs4pr); fr (j1=0; j1<num; j1++) { hthread[j1] = _beginthread (bell,0,(vid *)j1); universeller Zeiger! WaitFrMultipleObjects (NUM, (CONST HANDLE)hThread, TRUE, INFINITE); DeleteCriticalSectin (&tim->cs4pr); return; (MultiBellThreadCS.exe)
Thread-Synchrnisatin (III) #include <windws.h> Event-Kennung //genauer: winbase.h Thread-Blckierung (-Suspendierung) für msec Millisekunden: VOID Sleep(DWORD msec); Verknüpfg d. WaitFr...-Funktinen mit Ereignissen hne Handle: Event erzeugen, setzen, zurücksetzen: HANDLE CreateEvent (LPSECURITY_ATTRIBUTES lpthrattr, BOOL bmanreset,bool binitstate,lpctstr lpname); BOOL SetEvent (HANDLE handle);//event gesetzt(return!=0) BOOL ResetEvent(HANDLE handle);//ev.zurückgesetzt(r.!=0) Parameter: lpthrattr : Zeiger auf interne Struktur (betr.interprz.-kmmun.): NULL bmanreset : Erwartetes Zurücksetzen (TRUE= manuell : durch Aufruf v. ResetEvent; FALSE: autm., nach d. ersten Thread-Start) binitstate : lpname : Anfangszustand (TRUE: gesetzt; FALSE: zurückgesetzt) Zeiger auf Event-Namen( sz :"string terminated with a zer")
Bemerkungen zu den vrausgegangenen Flien: Htkey: Taste(nkmbinatin) zur Aktivierung einer bestimmten Rechner-(BS-) Funktin. Spl: Simultaneus Peripheral Operatins OnLine Nnpreemptive (cperative) Multitasking wird ins Deutsche als nicht-verdrängender Mehrprzeßbetrieb übertragen ( preemptin bezeichnet allg. das Vrkaufsrecht ). Thread wird ins Deutsche als Ausführungsstrang übertragen (engl. für: Faden, Garn, Zwirn ) Abwicklungsflge: Kleinste Verarbeitungseinheit (BS, Appl.), der Rechenzeit zugewiesen wird. Typische Größenangaben: max. 2028 Threads je 1MB Stack; 20 msec (=1/50 sec) je Time Slice Der Bezeichner cdecl ist (nur) insfern vn Interesse, als er die Standard-Einstellungen des Cmpilers wieder einsetzt, falls sie zuvr außer Kraft gesetzt wrden waren. Die Funktin DWORD WaitFrSingleObject(HANDLE handle,dword msec); wurde hier nicht näher besprchen, weil sie gegenüber WaitFrMultipleObjects (abgesehen vm Handle-Typ HANDLE gegenüber CONST HANDLE) hauptsächlich eine eingeschränkte Funktinalität bietet. CreateThread() hat gegenüber _beginthread() den Vrzug, daß sein Bezeichner, seine verwendeten Datentypen und sein "Outfit" zu einer "Familie" gehören (vgl. CreateWindw). Seine Nachteile sind (u.a.): Es benötigt mehr (und selten genutzte) Parameter, getrenntes Exit und Clse (Erlernen, Rechenzeit, Fehler-Quellen); Es erfrdert eine Thread-Funktin vm nicht-universellen Typ DWORD WINAPI ThreadPrc(); Es kann zu geringem Speicherschwund (small memry leaks) führen, wenn es mit der C- Laufzeitbiblithek verwendet wird. (BS bzw. Applikatin) Bsp.: s. SysPrg-Klausur WS 04/05