SDL: Threads

Aus Wikibooks

Threads dienen dazu, Aufgaben gleichzeitig oder scheinbar gleichzeitig durchzuführen. Es kann unter Umständen sinnvoll sein, Threads einzurichten, die sich beispielsweise um die Musik während eines Spieles kümmern. Bitte beachte Sie, dass Threads selbst durch den Wechsel von einem Thread zu einem anderen Rechenzeit kosten. Unter Umständen wird eine Anwendung mit Threads langsamer laufen als ohne. Echt Gleichzeitig können Aufgaben nur dann erfolgen, wenn Sie Gebrauch von einer zweiten CPU machen können.

Threads einrichten und verwalten[Bearbeiten]

Threads erzeugen[Bearbeiten]

Ein Thread wird repräsentiert durch eine Funktion folgender Gestalt

 int mein_thread (void* parameter);

Um einen Thread zu erzeugen, ruft man folgende Funktion auf:

 SDL_Thread *SDL_CreateThread (int (*thread_func)(void*), void *thread_parameter);

Diese Funktion erwartet obige Funktion als Argument, der Parameter thread_parameter wird direkt an den eigentlichen Thread übergeben.

Threads beenden[Bearbeiten]

Die Funktion

void SDL_KillThread (SDL_Thread *thread);

beendet den laufenden Thread sofort. Der Thread hat keine Möglichkeiten mehr, evtl. anstehende Aufräumarbeiten zu erledigen.

Warten auf das Ende[Bearbeiten]

Mit der Funktion

void SDL_WaitThread (SDL_Thread *thread, int *status);

wartet man auf das Beenden des Threads. Man wartet unendlich lange, wenn der Thread eine Endlosschleife implementiert. Der Rückgabewert der Threadfunktion wird in status übergeben.

Sonstiges[Bearbeiten]

Die Funktionen

 Uint32 SDL_GetThreadID(SDL_Thread *thread);

und

 Uint32 SDL_ThreadID (void);

übergeben eine 32-Bit Zahl, die den Thread eindeutig benennt.

Beispiel[Bearbeiten]

Das folgende Beispiel implementiert zwei Threads. Innerhalb der Threads werden Rechtecke an zufälligen Positionen erzeugt

 rect.x = 1 + (int) (630 * (rand() / (RAND_MAX + 1.0)));
 rect.y = 1 + (int) (470 * (rand() / (RAND_MAX + 1.0)));

und anschließend gezeichnet. Die Threads selbst werden mit

 t1 = SDL_CreateThread (thread1, screen);
 t2 = SDL_CreateThread (thread2, screen);

erzeugt, der zusätzliche Parameter ist hier der Zeiger auf die Surface des Bildschirmes. Drückt der Benutzer die Escape-Taste, so werden beide Threads beendet mit

 SDL_KillThread(t1);
 SDL_KillThread(t2);
#include <stdlib.h>
#include <SDL.h>
#include <SDL_thread.h>

int thread1 (void *p)
{ 
  SDL_Rect rect;
  SDL_Surface *s = (SDL_Surface*) p;
  rect.x = 100;
  rect.y = 100;
  rect.w = 10;
  rect.h = 10;
  while (1)
    {
      rect.x = 1 + (int) (630 * (rand() / (RAND_MAX + 1.0)));
      rect.y = 1 + (int) (470 * (rand() / (RAND_MAX + 1.0)));
      SDL_FillRect (s, &rect, SDL_MapRGB (s->format, 255, 0, 0));
      SDL_UpdateRect (s, rect.x, rect.y, rect.w, rect.h);
      SDL_Delay (100);
    }
  return 0;
} 

int thread2 (void *p)
{ 
  SDL_Rect rect;
  SDL_Surface *s = (SDL_Surface*) p;
  rect.x = 100;
  rect.y = 100;
  rect.w = 10;
  rect.h = 10;
  while (1)
    {
      rect.x = 1 + (int) (630 * (rand() / (RAND_MAX + 1.0)));
      rect.y = 1 + (int) (470 * (rand() / (RAND_MAX + 1.0)));
      SDL_FillRect (s, &rect, SDL_MapRGB (s->format, 0, 255, 0));
      SDL_UpdateRect (s, rect.x, rect.y, rect.w, rect.h);
      SDL_Delay (100);
    }
  return 0;
} 

int main (void)
{
  SDL_Surface *screen;
  SDL_Event e;
  SDL_Thread *t1, *t2;
  int quit = 0;
  if (SDL_Init (SDL_INIT_VIDEO) == -1)
    {
      printf ("Kann SDL nicht initialisieren: %s\n", SDL_GetError ());
      exit (1);
    }
  atexit (SDL_Quit);
  screen = SDL_SetVideoMode (640, 480, 16, SDL_SWSURFACE);
  if (screen == NULL)
    {
      printf ("Video Modus kann nicht eingerichtet werden: %s\n", SDL_GetError());
      exit (1);
    }
  t1 = SDL_CreateThread (thread1, screen);
  t2 = SDL_CreateThread (thread2, screen);
  while (SDL_WaitEvent (&e) && !quit)
    switch (e.type)
      {
        case SDL_KEYDOWN:
          if (e.key.keysym.sym == SDLK_ESCAPE)
            {
              SDL_KillThread(t1);
              SDL_KillThread(t2);
              quit = 1;
            }
        break;
      }
  return 0;
}

Bitte nicht stören[Bearbeiten]

Einleitendes Beispiel[Bearbeiten]

Das folgende Programm versucht, nacheinander die Zeichenketten "aaaaa" und "bbbbb" auszugeben, eine Pause einzulegen und dann wieder von vorne zu beginnen. Bitte beachten Sie, daß hier keine Video-Ausgabe erfolgt. Das Programm funktioniert rein im Shell-Modus ohne eigenes Fenster.

#include <stdlib.h>
#include <SDL.h>
#include <SDL_thread.h>

int thread1 (void *p)
{
  while (1)
    {
      printf ("a");
      SDL_Delay (10);
      printf ("a");
      SDL_Delay (10);
      printf ("a");
      SDL_Delay (10);
      printf ("a");
      SDL_Delay (10);
      printf ("a");
      SDL_Delay (10);
      printf("\n");
      SDL_Delay (200);
    }
  return 0;
}

int thread2 (void *p)
{
  while (1)
    {
      SDL_Delay (10);
      printf ("b");
      SDL_Delay (10);
      printf ("b");
      SDL_Delay (10);
      printf ("b");
      SDL_Delay (10);
      printf ("b");
      SDL_Delay (10);
      printf("\n");
      SDL_Delay (200);
    }
  return 0;
}

int main (void)
{
  SDL_Thread *t1, *t2;
  if (SDL_Init (0) == -1)
    {
      printf ("Kann SDL nicht initialisieren: %s\n", SDL_GetError ());
      exit (1);
    }
  t1 = SDL_CreateThread (thread1, NULL);
  t2 = SDL_CreateThread (thread2, NULL);
  SDL_Delay (2000);
  SDL_KillThread (t1);
  SDL_KillThread (t2);
  SDL_Quit ();
  return 0;
}

Führen wir das Programm aus, passiert etwas sonderbares:

ababababab

ababababab

ababababab

ababababab

ababababab

ababababab

ababababab

Von einem "aaaaa", "bbbbb", wie wir es uns wünschen, ist wenig zu sehen, beide Threads überlagern sich und machen ihre Ausgaben, während der andere Thread jeweils ebenfalls Daten ausgibt. Wünschenswert wäre ein Modus, der Bitte nicht stören heißt und bedeutet, daß ein Thread exklusiv ausgeführt wird, ungestört von einem anderen Thread. Ein solcher kritischer Bereich (critic section) kann mit Hilfe eines Mutex erzeugt werden. Ein Mutex ist eine Art Fähnchen, welches derjenige Thread hochhält, der ungestört seine Arbeit verrichten will. Hinterher wird das Fähnchen wieder abgenommen, es werden wieder Störungen zugelassen.

Mutex erzeugen, entfernen[Bearbeiten]

Ein Mutex wird erzeugt, indem

 SDL_mutex* SDL_CreateMutex (void);

aufgerufen wird.

Um ein Mutex wieder zu entfernen, kann

 void SDL_DestroyMutex (SDL_mutex *mutex);

aufgerufen werden.


Mutex setzen[Bearbeiten]

Um mitzuteilen, daß der folgende Bereich kritisch ist und exklusiv ausgeführt wird, dient die Funktion

 int SDL_mutexP (SDL_mutex *mutex);

Die Funktion gibt 0 bei Erfolg zurück, sonst -1.

P steht für "proberen" und V im folgenden Abschnitt für "verhogen", dies sind die üblichen Bezeichnungen der beiden Funktionen nach ihrem Erfinder Dijkstra. Zur besseren Lesbarkeit des Codes existiert ein Makro namens SDL_LockMutex (mutex), welches diese Funktion ersetzt.

Mutex löschen[Bearbeiten]

Nach Verlassen eines kritischen Bereiches wird die Funktion

 int SDL_mutexV (SDL_mutex *mutex);

aufgerufen. Die Rückgabewerte sind dieselben wie bei SDL_mutexP. Zu dieser Funktion existiert ein Makro namens SDL_UnlockMutex (mutex).

Beipiel mit Mutex[Bearbeiten]

#include <stdlib.h>
#include <SDL.h>
#include <SDL_thread.h>

int thread1 (void *p)
{ 
  SDL_mutex *m = (SDL_mutex*) p;
  while (1)
    {
      SDL_LockMutex (m);
      printf ("a");
      SDL_Delay (10);
      printf ("a");
      SDL_Delay (10);
      printf ("a");
      SDL_Delay (10);
      printf ("a");
      SDL_Delay (10);
      printf ("a");
      SDL_Delay (10);
      printf("\n");
      SDL_UnlockMutex (m);
      SDL_Delay (200);
    }
  return 0;
}

int thread2 (void *p)
{ 
  SDL_mutex *m = (SDL_mutex*) p;
  while (1)
    {
      SDL_LockMutex (m);
      printf ("b");
      SDL_Delay (10);
      printf ("b");
      SDL_Delay (10);
      printf ("b");
      SDL_Delay (10);
      printf ("b");
      SDL_Delay (10);
      printf ("b");
      SDL_Delay (10);
      printf("\n");
      SDL_UnlockMutex (m);
      SDL_Delay (200);
    }
  return 0;
}

int main (void)
{ 
  SDL_Thread *t1, *t2;
  SDL_mutex *mutex;

  if (SDL_Init (0) == -1)
    {
      printf ("Kann SDL nicht initialisieren: %s\n", SDL_GetError ());
      exit (1);
    }

  mutex = SDL_CreateMutex ();
  t1 = SDL_CreateThread (thread1, mutex);
  t2 = SDL_CreateThread (thread2, mutex);
  SDL_Delay (2000);
  SDL_KillThread (t1);
  SDL_KillThread (t2);
  SDL_DestroyMutex (mutex);
  SDL_Quit ();
  return 0;
}

Die Ausgabe des Programmes ist nun:

aaaaa
bbbbb
aaaaa
bbbbb
aaaaa
bbbbb
aaaaa
bbbbb

wie gewünscht.

Semaphoren[Bearbeiten]