hubblec4 hat geschrieben:Wow, OK, das wusste ich nicht. Also wenn der Main.Thread die Worker-Var(eigentlich Property) Exit:=true setzt, gibt es keine Probleme wenn in dem selben Augenblick im Worker-Thread die var abgefragt wird(verändert wird sie dort eigentlich nicht)? Und das muss auch nicht in ein try-Excedpt-Block oder mit Crital-section abgefangen werden?
Gilt das nur für Booleans, oder kann da auch ein Integer oder String verändert werden?
Eine leseoperation ist atomar, und eine schreiboperation ist atomar (zumindest bei grunddatentypen, bei anderem weiß ichs nicht, denke aber mal nicht. Bei Integern wirds schon etwas komplizierter, z.b.
Code: Alles auswählen
for i:=0 to 1000000 do
begin
sharedInt := sharedInt + 1;
end;
Die zweisung besteht aus zwei operationen auf sharedInt, 1. Lesen, 2. Schreiben. Wenn jetzt ein zweiter thread die variable verändert genau zwischen dem lesen und dem schreiben, geht die schreiboperation des zweiten threads verloren. Wenn du die schleife auf 4 threads ausführen würdes (mit sharedInt als globale variable), würde mit jedem durchlauf ne andere zahl rauskommen, aber wahscheinlich immer knapp unter 4000000
Strings sind viel komplizierter, die sind referenzgezählt, machen copy on write, und ganz viel anderen kram, da kann sogar richtig was kaputt gehen wenn zwei threads versuchen gleichzeitig drauf zuzugreifen.
hubblec4 hat geschrieben:Critical sections hatte ich auch schon bissl was drüber gelesen aber nicht weiter verfolgt. Das sollte ich dann wohl auch mal genauer anschauen.
Sagen wir mal du möchtest ein message system implementieren, dann könntest du sowas machen:
Code: Alles auswählen
TThread1 = class(TThread)
private
FMessageCS: TRTLCriticalSection;
FMessages: TStringList;
...
constructor TThread1.create(createSuspended: Boolean);
begin
...
InitCriticalSection(FMessageCS);
FMessages := TStringList.Create;
...
end;
destructor TThread1.Destroy;
begin
...
DoneCriticalSection(FMessageCS);
FMessages.Free;
...
end;
procedure TThread1.sendMessage(Message: String);
begin
EnterCrtiticalSection(MessageCS);
try
FMessages.add(Message);
finally
LeaveCriticalSection(MessageCS);
end;
end;
...
// Hauptschleife:
EnterCrtiticalSection(MessageCS);
try
for message in FMessages do
...
FMessages.Clear;
finally
LeaveCriticalSection(MessageCS);
end;
Hat praktisch keine kosten, außer wenn zufällig addmessage aufgerufen wird wenn man in der abfrage ist (oder anders rum), dann muss der prozess der als zweites kam warten.
Problematisch wird es wenn mehrere CS und abhängigkeiten auftreten, z.B.
Code: Alles auswählen
Thread1 -> Lock CS 1
Thread2 -> Lock CS 2
Thread1 -> Lock CS 2
Thread2 -> Lock CS 1
Jetzt wartet Thread1 auf Thread2 und anders rum, ein Deadlock. Ziemlich lästig, passiert schneller als man denkt (vor allem in einem event basiertem system). Als grundregel, CS immer klein halten (als wenn man z.B. lange braucht um die messages zu verarbeiten, dann die rauskopieren, liste leeren, CS freigeben und auf der kopie arbeiten), und möglichst wenig CS auf einmal aquirieren
hubblec4 hat geschrieben:Mir ist schon aufgefallen das ein WriteLn() bissl Zeit kostet und auch so ein Synchronize() braucht etwas Zeit. Und je öfter das ausgeführt wird um so öfter unterbricht es den Worker.Thread.
Locken ist auch nicht unbedingt kostenlos, vor allem wenn sehr oft versucht wird zuzugreifen kann es sein das du länger im CS wartest als du tatsächlich arbeitest (dann wirds durch das lock zwangsserialisiert), kommt immer auf das problem an, parallele programierung ist leider nicht einfach.
WriteLn besteht halt aus ein paar operationen, zum einen wird in eine datei geschrieben, was bedeutet der aktuelle prozess wird angehalten, der kernel übernimmt die schreiboperation, der prozess wird fortgesetzt, dann wird geflusht, dafür wird der prozess wieder angehalten, der kernel schreibt seinen dateisystem buffer in die pipe, und erst wenn das erfolgreich war ist die writeln operation zu ende. Wenn du z.B. einen bruteforcer schreibst und dir jedes zwischenergebnis ausgeben lässt, kann dich das gut nen faktor 1000 an geschwindigkeit kosten