SDL Thread Beispiel: Unterschied zwischen den Versionen
I0n0s (Diskussion | Beiträge) |
DGLBot (Diskussion | Beiträge) K (Der Ausdruck ''<pascal>(.*?)</pascal>'' wurde ersetzt mit ''<source lang="pascal">$1</source>''.) |
||
Zeile 11: | Zeile 11: | ||
Im Pseudocode: | Im Pseudocode: | ||
− | <pascal> | + | <source lang="pascal"> |
var //Links von der Seite | var //Links von der Seite | ||
Links : Array of String; | Links : Array of String; | ||
Zeile 31: | Zeile 31: | ||
end; | end; | ||
end; | end; | ||
− | </ | + | </source> |
'''LadeHerunter''' lädt das Dokument herunter und speichert sie auf die Festplatte.<br> | '''LadeHerunter''' lädt das Dokument herunter und speichert sie auf die Festplatte.<br> | ||
'''AnalysiereDatei''' analysiert das Dokument auf Links und fügt sie dem Array Links hinzu. | '''AnalysiereDatei''' analysiert das Dokument auf Links und fügt sie dem Array Links hinzu. | ||
Zeile 44: | Zeile 44: | ||
---- | ---- | ||
Das Auslagern in einen neuen Thread ist ganz einfach: | Das Auslagern in einen neuen Thread ist ganz einfach: | ||
− | <pascal>var Thread: PSDL_Thread; | + | <source lang="pascal">var Thread: PSDL_Thread; |
begin | begin | ||
Thread:=SDL_CreateThread(@Download,nil); | Thread:=SDL_CreateThread(@Download,nil); | ||
− | end;</ | + | end;</source> |
Der ganze Webspider sieht jetzt so aus: | Der ganze Webspider sieht jetzt so aus: | ||
− | <pascal> | + | <source lang="pascal"> |
var //Links von der Seite | var //Links von der Seite | ||
Links : Array of String; | Links : Array of String; | ||
Zeile 98: | Zeile 98: | ||
end; | end; | ||
end; | end; | ||
− | </ | + | </source> |
So, jetzt laden wir gerade 2 Dateien 'gleichzeitig' herunter.<br> | So, jetzt laden wir gerade 2 Dateien 'gleichzeitig' herunter.<br> | ||
Zeile 116: | Zeile 116: | ||
<br> | <br> | ||
Der ganze Webspider sieht jetzt so aus: | Der ganze Webspider sieht jetzt so aus: | ||
− | <pascal>var Thread: PSDL_Thread; | + | <source lang="pascal">var Thread: PSDL_Thread; |
begin | begin | ||
Thread:=SDL_CreateThread(@Download,nil); | Thread:=SDL_CreateThread(@Download,nil); | ||
− | end;</ | + | end;</source> |
Der ganze Webspider sieht jetzt so aus: | Der ganze Webspider sieht jetzt so aus: | ||
− | <pascal> | + | <source lang="pascal"> |
var //Links von der Seite | var //Links von der Seite | ||
Links : Array of String; | Links : Array of String; | ||
Zeile 174: | Zeile 174: | ||
end; | end; | ||
end; | end; | ||
− | </ | + | </source> |
Wir haben unseren Mutex erstellt. Ab jetzt kann nur ein Thread gleichzeitig auf Links zugreifen. | Wir haben unseren Mutex erstellt. Ab jetzt kann nur ein Thread gleichzeitig auf Links zugreifen. | ||
Da es nur eine kurzer Zugriff ist, geht dadurch auch nicht viel Zeit verloren.<br> | Da es nur eine kurzer Zugriff ist, geht dadurch auch nicht viel Zeit verloren.<br> | ||
Zeile 191: | Zeile 191: | ||
[[SDL_SemWait]] und [[SDL_SemPost]].<br> | [[SDL_SemWait]] und [[SDL_SemPost]].<br> | ||
Alles verstanden? Dann einbauen: | Alles verstanden? Dann einbauen: | ||
− | <pascal> | + | <source lang="pascal"> |
var //Links von der Seite | var //Links von der Seite | ||
Links : Array of String; | Links : Array of String; | ||
Zeile 253: | Zeile 253: | ||
end; | end; | ||
end; | end; | ||
− | </ | + | </source> |
== Finale Worte == | == Finale Worte == |
Aktuelle Version vom 10. März 2009, 19:09 Uhr
Inhaltsverzeichnis
Multithreading in SDL
Benutzung der Synchronmöglichkeiten
Vorwort
Das Ziel des Artikel soll es sein, die Möglichkeiten zur Synchronisierung von SDL_Threads anhand von (hoffentlich) sinnvollen Beispielen zu verdeutlichen.
Als übergreifendes Beispiel wollen wir einen Webspider konstruieren.
Funktionsweise eines Webspiders
Die Funktion eines Webspiders ist recht einfach. Er lädt Webseiten herunter und analysiert sie auf weitere Links. Dies macht er solange bis keine weiteren Links mehr vorhanden sind.
Im Pseudocode:
var //Links von der Seite
Links : Array of String;
//Datei wo die aktuelle Seite gespeichert ist.
Datei : String;
i : Integer;
Done : Boolean;
begin
Done :=False;
Setlength(Links,1);
Links[0] :='http://wiki.delphigl.com/index.php/Hauptseite';
i :=0;
while not Done do
begin
Datei :=LadeHerunter(Links[i]);
AnalysiereDatei(Datei);
Inc(i);
if i>High(Links) then Done:=True;
end;
end;
LadeHerunter lädt das Dokument herunter und speichert sie auf die Festplatte.
AnalysiereDatei analysiert das Dokument auf Links und fügt sie dem Array Links hinzu.
Schneckentempo
Der aktuelle Webspider funktioniert zwar, ist aber ziemlich langsam.
Weshalb?
Ganz einfach, es wird immer nur eine Datei heruntergeladen und analysiert.
Als Lösung können wir anfangen mehrere Dateien parallel herunterzuladen.
Arbeitsbeschaffungsmassnahme
Das Auslagern in einen neuen Thread ist ganz einfach:
var Thread: PSDL_Thread;
begin
Thread:=SDL_CreateThread(@Download,nil);
end;
Der ganze Webspider sieht jetzt so aus:
var //Links von der Seite
Links : Array of String;
Link : String;
Datei : String;
Done : Boolean;
//unser Thread
Thread: PSDL_Thread;
begin
Done :=False;
Setlength(Links,1);
Links[0] :='http://wiki.delphigl.com/index.php/Hauptseite';
Thread :=SDL_CreateThread(@Download,nil);
while not Done do
begin
if High(Links)=-1 then
begin
Done:=True;
break;
end;
Link := Links[High(Links)];
SetLength(Links,High(Links));
Datei :=LadeHerunter(Link);
AnalysiereDatei(Datei);
end;
//warten bis der Thread auch wirklich beendet wurde
SDL_WaitThread(Thread);
SDL_DestroyMutex(Muted);
end;
function Download: Integer;
var Datei : String;
Link : String;
begin
while not Done do
begin
if High(Links)=-1 then
begin
Done:=True;
break;
end;
Link := Links[High(Links)];
SetLength(Links,High(Links));
Datei :=LadeHerunter(Link);
AnalysiereDatei(Datei);
end;
end;
So, jetzt laden wir gerade 2 Dateien 'gleichzeitig' herunter.
Was passiert aber wenn die beiden Threads gleichzeitig den nächsten Links auslesen?
So würde die nächste Datei doppelt heruntergeladen und analysiert.
Wenn man das Tutorial von Lossy eX gelesen hat, weiss man ja das sowas passieren kann.
Wir brauchen also eine Kontrollstruktur die das verhindert.
Der Türsteher
In SDL ist es ein Mutex. Ein Mutex ist im Prinzip ein einfacher Boolean.
Wenn dieser False (0) ist, darf man noch nicht zugreifen.
Erst wenn er wieder freigegeben ist, darf man weitermachen.
Dazu existieren SDL_LockMutex und SDL_UnLockMutex.
SDL_LockMutex schaut erst ob der Boolean true ist, wenn nicht wartet sie, und setzt ihn dann auf falsche.
SDL_UnLockMutex setzt einfach den Wert auf true.
Der ganze Webspider sieht jetzt so aus:
var Thread: PSDL_Thread;
begin
Thread:=SDL_CreateThread(@Download,nil);
end;
Der ganze Webspider sieht jetzt so aus:
var //Links von der Seite
Links : Array of String;
Done : Boolean;
i : Integer;
//unser Threads
Thread: Array of PSDL_Thread;
//der Mutex
Mutex : PSDL_Mutex;
begin
Done :=False;
Setlength(Links,1);
Links[0] :='http://wiki.delphigl.com/index.php/Hauptseite';
Mutex :=SDL_CreateMutex;
SetLength(Thread,2);
for i:=0 to High(Thread) do
Thread[i] :=SDL_CreateThread(@Download,nil);
while not Done do
begin
SDL_Wait(500);
end;
//warten bis der Threads auch wirklich beendet wurde
for i:=0 to High(Thread) do
SDL_WaitThread(Thread[i]);
SDL_DestroyMutex(Muted);
end;
function Download: Integer;
var Datei : String;
Link : String;
begin
while not Done do
begin
SDL_LockMutex(Mutex);
try
if High(Links)=-1 then
begin
Done:=True;
break;
end;
Link := Links[High(Links)];
SetLength(Links,High(Links));
finally
SDL_UnLockMutex(Mutex);
end;
Datei :=LadeHerunter(Link);
AnalysiereDatei(Datei);
end;
end;
Wir haben unseren Mutex erstellt. Ab jetzt kann nur ein Thread gleichzeitig auf Links zugreifen.
Da es nur eine kurzer Zugriff ist, geht dadurch auch nicht viel Zeit verloren.
Die try...finally-Blöcke sind dafür zuständig, dass keine Deadlocks enstehen.
Soziale Verantwortung
Jetzt ist unser Webspider im Prinzip fertig. Man kann beliebig viele Threads erstellen und den Server voll auslasten.
Nur gibt es kaum eine schnellere Methode um seine IP von Phoebius für delphigl.com zu sperren, als diesen Webspider mit 100 Threads loszulassen.
Also Reduzieren wir einfach die Anzahl der gleichzeitigen Zugriffe auf den Server auf 50.
Dies heist aber nicht, dass wir die Anzahl der Threads reduzieren. Diese greifen ja nicht ständig sondern nur kurz auf den Webserver zu.
Dafür gibt es eine weitere Strukur namens Semaphore.
Diese ist kein einfacher Boolean sondern ein Integerwert. Wenn dieser grösser Null ist, darf man zugreifen, andernfalls muss man warten.
SDL stellt dafür folgende Funktionen zur Verfügung:
SDL_SemWait und SDL_SemPost.
Alles verstanden? Dann einbauen:
var //Links von der Seite
Links : Array of String;
Done : Boolean;
i : Integer;
//unser Threads
Thread: Array of PSDL_Thread;
//der Mutex
Mutex : PSDL_Mutex;
Semaphore : PSDL_Semaphore;
begin
Done :=False;
Setlength(Links,1);
Links[0] :='http://wiki.delphigl.com/index.php/Hauptseite';
Mutex :=SDL_CreateMutex;
//50 ist die max. Anzahl der gleichzeitig aktiven Threads
Semaphore :=SDL_CreateSemaphore(50);
SetLength(Thread,100);
for i:=0 to High(Thread) do
Thread[i] :=SDL_CreateThread(@Download,nil);
while not Done do
begin
SDL_Wait(500);
end;
//warten bis der Threads auch wirklich beendet wurde
for i:=0 to High(Thread) do
SDL_WaitThread(Thread[i]);
SDL_DestroyMutex(Muted);
SDL_DestroySemaphore(Semaphore);
end;
function Download: Integer;
var Datei : String;
Link : String;
begin
while not Done do
begin
SDL_SemWait(Semaphore);
try
SDL_LockMutex(Mutex);
try
if High(Links)=-1 then
begin
Done:=True;
break;
end;
Link := Links[High(Links)];
SetLength(Links,High(Links));
finally
SDL_UnLockMutex(Mutex);
end;
Datei :=LadeHerunter(Link);
finally
SDL_SemPost(Semaphore);
end;
AnalysiereDatei(Datei);
end;
end;
Finale Worte
Wir haben jetzt einen passablen kleinen Webspider gebaut (wenn ihr die Funktion AnalysiereDatei noch einbaut :p ) und hoffentlich das Konzept der Threads in SDL verstanden.
Das Erstellen und Arbeiten mit Threads ist kein Geheimnis. Es ist nur schwer etwas zu finden was man in einem Thread machen könnte ohne durch die Synchronisierung die Vorteile zu verlieren.