Dit is een archief-pagina

Dit is een archief-pagina. Ik werk deze pagina niet meer actief bij.
Informatie kan verouderd zijn, en links lopen mogelijk dood.
Maar, misschien heeft iemand er nog wat aan.


CLWP Thread Library

[English]This page is also available in English[English]

Ik wilde wel eens spelen met multi-threading (een soort multi-tasking, maar dan vanuit een programma, waarbij meerdere 'threads' binnen dezelfde geheugenruimte lopen, en dus bij elkaars data kunnen). Eerst gekeken naar bestaande oplossingen als pthreads en GNU-pth (zie onderaan voor mijn vondsten en ervaringen), maar dat was geen onverdeeld succes.

Ik zocht zo'n library om samen met de Allegro game library te gebruiken bij spelletjes, om meerdere figuren (spelers, of computer-bestuurde personen of objecten) op een eenvoudige, onafhankelijke manier te kunnen programmeren. Bijvoorbeeld de baby-pinguins in mijn Pinpin spelletje (geschreven in Allegro) kunnen op deze manier onafhankelijk van de ouder terugzwemmen naar hun beginpositie. Eigenlijk zou ik deze functionaliteit graag in Allegro zelf vinden!

Met name pthreads vertaald prima, maar bleek overshoot te zijn voor mijn doel (ik wilde het in mijn spelletjes gaan gebruiken, om de computer-speler onafhankelijk meerdere figures te kunnen laten 'spelen', ieder in een eigen, onafhankelijke thread). De reden is dat pthreads echt volledig pre-emptive multi-threading is, dat wil zeggen dat dit met behulp van een timer huidige zaken onderbreekt om de volgende thread te laten lopen. Nadeel is dan dat bijvoorbeeld de standaard library (en alle andere code) het aan moet kunnen om eventueel meerdere keren 'tegelijk' te draaien (moet re-entrant zijn). Eigenlijk zocht ik meer iets in 'co-operative' multi-threading (vergelijkbaar met co-routines), waar taken expliciet aangeven wanneer naar de volgende taak geschakeld kan worden.

Ben nu dus bezig met een eigen implementatie genaamd CLWP (Co-operative Ligth-Weight Processes), gebaseerd op LWP, een light-weight threading package van Josh Turpen bedoeld voor de DOS/DJGPP compiler. Was ook pre-emptive, maar dat heb ik er uit gestript. De hele LWP library code was maar twee kleine files (een C, een assembly) dus dat was vrij eenvoudig te doen (paar uur werk, na het bestuderen van de code). Het schrijven en runnen van de documentatie en test-code was meer werk. Vooral het correct maken van de timing tests (example 4) ging een keer goed mis. Maar: ik heb nu de task switch getimed op minder dan 0.25 microseconde (1GHz Pentium-III, runtime measurement in Windows-ME omgeving).

Een eerste 'final release' kan je downloaden: clwp13.zip file versie 1.3 (43 Kbyte) voor GCC. Uiteraard zonder garantie....... Getest onder Dev_C++ 4.0/5.0 met GCC 2.95.3 en GCC 3.2, en ook DOS/DJGPP met GCC 2.95.3 en Linux/GCC 3.2.1 (maar zou ook moeten werken op andere Intel/AMD i86-gebaseerde platforms), en onder andere op windows/ME en Windows/2000. Nu met een html versie van het clwp manual.

Voor Microsoft Visual C 6 is de assembler syntax anders, en deze download niet direct bruikbaar, maar van Ade Adewole uit Canada deze aangepaste versie van CLWP 1.1 (35 KB). Bedankt!

Er is ook een port van CLWP13 naar 16-bit Real-Mode DOS met de Borland C++ v3.1 compiler, dankzij Muhammad Hasnain die heel wat werk heeft gestoken in het omschrijven van CLWP voor dit doel. De hele source gebruikt wel een zware mix van near en far pointers, en alle memory models behalve Huge worden ondersteund. Let op: 'proc()' is far en neemt een far parameter (niet relevant als je programmeert in het Large Memory model, omdat daar far de default is). Opmerking: dit is niet een 'echte' 16-bit poort, omdat extended registers en instructies van de 80386 gebruikt worden. De 16-bit slaat met name op het programmeermodel.

Een klein voorbeeldje van een programma gebruik makend van de clwp library:

#include "clwp.h"
#include <stdio.h>

#define MAX_PROC 3


void proc(void *param)         /* this proc will be started as thread */
{   while(1)                   /* multiple times in parallel */
    {   printf("PROC %d\n", *((int *) param) );
        clwpYield();           /* switch to next thread */
    }
}

int main()
{
    volatile int i;
    int ids[MAX_PROC];         /* to store thread IDs */
    int a=1, b=2, c=3;         /* 'data' for each thread */
    printf("\nCLWP Example.\n");
    printf("This program spawns 3 threads that each print messages.\n");
    fflush(stdout);

    if(clwpInit(1))            /* initialise the threading system */
    {                          /* and create 3 extra threads */
       ids[0] = clwpSpawn(proc, &a, 4096, 1, TRUE);
       ids[1] = clwpSpawn(proc, &b, 4096, 1, TRUE);
       ids[
2] = clwpSpawn(proc, &c, 4096, 1, TRUE);
       for(i=0;i<5;i++)        /* go looping, activating all threads */
       {
           printf("MAIN\n"); clwpYield();
       }

       clwpKill(ids[0]);       /* OK, work is done, let's go home */
       clwpKill(ids[1]);
       clwpKill(ids[2]);
   }
   printf("Ready\n");
   return(0);                  /* clwpExit call not needed (auto) */
}

/* end of file */

Ziet er eenvoudig uit, nietwaar? Uiteraard ondersteunt clwp meer functies, zoals semaforen, threads tijdelijk stoppen/laten doorgaan, verschillende prioriteiten, en zo voort. Snelheid is ook heel behoorlijk, bij een eenvoudige tijdsmeting blijkt een taskswitch niet of nauwelijks meer tijd te kosten als een functie-call (zie de clwp manual in de zip file voor meer details). Oftewel, ook voor snelheid geen reden meer om je in moeilijke bochten te wringen: gewoon meerdere taken parallel laten lopen. Programmeert in een aantal gevallen een stuk logischer. Feedback is welkom!


Alternatieven

pthreads

Eerste poging: Nu heeft Windows vast wel iets voor multi-threading, maar ik kies liever voor een standaard, hier de POSIX standaard implementatie. Deze is voor Dev-C++ (of eigenlijk algemeen voor de MingW compiler) te vinden op http://sources.redhat.com/pthreads-win32/ . Hier is de 800 Kbyte zipfile pthreads-2002-03-02.exe te vinden met de volledige sources, test (die gelijk als voorbeelden dienen) etc (of download alleen de voorvertaalde libraries, kijk in ftp://sources.redhat.com/pub/pthreads-win32/dll-latest). Vertaalde en draaide in een keer prima! Zeker aan te raden als je wel pre-emptive multi-tasking zoekt. Wat ik dus niet deed... Artikel over POSIX threads is te vinden op de site: Programming POSIX Threads

GNU pth

Ook is er een goede GNU implementatie voor cooperative multi-threading; GNU Pth. Ziet er in eerste instantie goed uit, maar werkt alleen op basis van een POSIX/UNIX-achtige C configuratie, zoals bijvoorbeeld die van Cygwin. Onder Dev-C++/MingW kreeg ik hem niet simpel aan de praat, met name niet het configuratieprogramma dat uitzoekt wat precies het C systeem allemaal ondersteunt (sockets, etc). Dit moet draaien op een UNIX shell zoals 'bash', en draait niet (of in ieder geval niet goed) onder Windows/DOS prompts.

Op mijn werk heb ik wel de Cygwin GNU compiler, en daar draaide het inderdaad vlekkeloos. Nadeel is dat dan ook altijd de grote dll met de Cygwin runtime meegeleverd moet worden, en dat vind ik voor spelletjes te groot worden.

Dus: toch maar zelf iets geprobeerd...