![]() |
Delphi einfaches Multithread Beispiel.
Hier ein einfaches Multithread Beispiel ohne viel Schnörkel:
Beschreibung: Es wird Speicher reserviert für 20 Threads. 20 Threads werden gestartet über ein array of TmyThread. Gestartete Threads schreiben in ein Memo ihre Thread ID. Die Threads zählen ein integer hoch in Form1.caption. Je mehr threads gestartet desto schneller. Wird ein Thread geschlossen, so entfernt er seine Thread ID aus dem Memo. Über den zweiten Button können alle Threads über eine Schleife gleichzeitig beendet werden. Wer Verbesserungsvorschläge hat kann diese natürlich hinzufügen :thumb: Es werden benötigt: 2 Buttons und 1 Memo. --Die aktuelle korrigierte Version findet sich weiter ![]()
Delphi-Quellcode:
unit Unit1;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Comctrls, System.ImageList, Vcl.ImgList, Vcl.StdCtrls; type TmyThread = class(TThread) procedure Execute; override; procedure Synthreads; procedure Syni; end; TForm1 = class(TForm) Button1: TButton; Button2: TButton; Memo1: TMemo; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; i:integer =0; var threads: array of cardinal; var meinThread: array of TmyThread; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var i : integer; begin SetLength(meinthread, 20); for i := 0 to high(meinThread) do meinThread[i]:= TmyThread.Create(false); end; { TmyThread } procedure TmyThread.Execute; var z: integer; var cnt: integer; begin inherited; SetLength(threads, length(threads)+1); threads[(high(threads))] := self.ThreadID; SYNCHRONIZE(Self.Synthreads); for z := 0 to 50 do begin inc(i); sleep(random(500)); Synchronize(Syni); if Terminated then break; end; for cnt:= low(threads) to High(threads) do if threads[cnt] = self.ThreadID then begin threads[cnt] := threads[high(threads)]; SetLength(threads, length(threads)-1); end; Synchronize(Synthreads); end; procedure TmyThread.Syni; begin form1.Caption:= inttostr(i); end; procedure TmyThread.Synthreads; var line: integer; begin form1.memo1.clear; for line := Low(threads) to High(threads) do form1.Memo1.Lines.Add(inttostr(threads[line])); end; procedure TForm1.Button2Click(Sender: TObject); var i:integer; begin if Length(meinThread) >0 then for I := 0 to high(meinThread) do if ((meinThread[i] <> nil ) and (meinThread[i].Terminated = false)) then meinThread[i].Terminate; end; end. |
AW: Delphi einfaches Multithread Beispiel.
Ich bin kein MuiThreading-Profi aber ich finde das hier solltest du nicht als Beispiel posten.
Ich würde es grundlegend anders machen und Synchronize komplett rausschmeißen. Sendmessage/Postmessage und dann abarbeiten. Dann würde ich auch niemals einen Thread mit Terminate beenden. Eher Events oder so. Und dann dieser Vergleich auf False. Von globalen Variablen fange ich gar nicht erst an. Ich werde jetzt sicher wieder von allen Seiten angemacht, weil ich nur am meckern bin. Aber das ist kein guter Beispielcode. |
AW: Delphi einfaches Multithread Beispiel.
Die Nutzung von globalen Variablen wird nicht gern gesehen. Es gibt keinen Grund, die nicht zumindest in den Implementationsabschnitt zu verfrachten. Außerdem ist hier das falsche Schlüsselwort var verwendet, wobei threadvar keine automatische Speicherverwaltung auch nicht für nicht-primitive Nicht-Klassen hat. Und die Namen der Variablen überschneiden sich. Man würde allgemein keine Variable i nennen, die keine lokale Zählvariable ist.
|
AW: Delphi einfaches Multithread Beispiel.
Guten Abend :-)
Zitat:
Zitat:
Zitat:
Aber jetzt zum interessanten Teil - ich habe mal kommentiert:
Delphi-Quellcode:
procedure TmyThread.Execute;
var z: integer; cnt: integer; begin inherited; // Unnötig SetLength(threads, length(threads)+1); // Gefährlich - SetLength ist NICHT thread-safe! threads[(high(threads))] := self.ThreadID; SYNCHRONIZE(Self.Synthreads); for z := 0 to 50 do begin inc(i); // Integer-zugriff ist atomar und damit i.O. (soweit ich weiß) sleep(random(500)); Synchronize(Syni); if Terminated then break; end; for cnt:= low(threads) to High(threads) do if threads[cnt] = self.ThreadID then begin threads[cnt] := threads[high(threads)]; SetLength(threads, length(threads)-1); // Auch hier eine race condition, mehrere Thread könnten SetLength gleichzeitig aufrufen! // Außerdem ein Speicherleck, der Thread wird nicht freigegeben end; Synchronize(Synthreads); end; procedure TForm1.Button2Click(Sender: TObject); var i:integer; begin if Length(meinThread) >0 then // Überflüssig, die Schleife läuft ohne Elemente eh nicht for I := 0 to high(meinThread) do // Vergleich auf nil _aktuell_ überflüssig, da du es nie auf nil setzt. Zudem auch der check auf Terminated überflüssig - zweimal aufrufen schadet nicht. if ((meinThread[i] <> nil ) and (meinThread[i].Terminated = false)) then meinThread[i].Terminate; end; |
AW: Delphi einfaches Multithread Beispiel.
Guten Morgen!
Habe alles nochmal überarbeitet. Setlength rausgeworfen und die Thread id mit in das array gepackt. Wenn der Thread durchgelaufen ist, wird die ID auf 0 gesetzt, und erscheint dann auch im memo nicht mehr. Kritik erwünscht !
Delphi-Quellcode:
unit Unit1;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Comctrls, System.ImageList, Vcl.ImgList, Vcl.StdCtrls; type TmyThread = class(TThread) private fmeineThreadID: cardinal; public procedure Execute; override; procedure Synthreads; procedure Syni; property meineThreadID: Cardinal Read fmeineThreadID Write fmeineThreadID; end; TForm1 = class(TForm) Button1: TButton; Button2: TButton; Memo1: TMemo; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; i:integer =0; var meinThread: array of TmyThread; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var i : integer; begin SetLength(meinthread, 20); for i := 0 to high(meinThread) do meinThread[i]:= TmyThread.Create(false); end; { TmyThread } procedure TmyThread.Execute; var z: integer; begin fmeineThreadID := ThreadID; SYNCHRONIZE(Self.Synthreads); for z := 0 to 50 do begin inc(i); sleep(random(500)); Synchronize(Syni); if Terminated then break; end; fmeineThreadID:= 0; Synchronize(Synthreads); FreeOnTerminate := true; end; procedure TmyThread.Syni; begin form1.Caption:= inttostr(i); end; procedure TmyThread.Synthreads; var line: integer; begin form1.memo1.clear; for line := Low(meinThread) to High(meinThread) do if meinThread[line].meineThreadID <> 0 then form1.Memo1.Lines.Add(inttostr(meinThread[line].ThreadID)); end; procedure TForm1.Button2Click(Sender: TObject); var i:integer; begin for I := 0 to high(meinThread) do if ((meinThread[i] <> nil ) and (meinThread[i].Terminated = false)) then meinThread[i].Terminate; end; end. |
AW: Delphi einfaches Multithread Beispiel.
Natürlich darf man Synchronize verwenden. Das ist dann wie ein Chef der die ganze Zeit über die Schulter schaut und sich dann beschwert, daß er sich um alles kümmern muß.Ein Thread ist erst dann sinnvoll wenn er "autark gedacht" wird. Wenn er länger läuft darf er messages versenden die dann vom Mainthread ausgewertet werden, das war es aber auch schon. Ist nicht ganz simpel aber machbar.
Gruß K-H |
AW: Delphi einfaches Multithread Beispiel.
Oje...
Ich werde keine Kritik schreiben - es ist noch kein Meister von Himmel gefallen...
Delphi-Quellcode:
Daher: Meine CodeRage2020 Session: Threads & Queues...
for var i:=0 to 19 do
TTask.Run() |
AW: Delphi einfaches Multithread Beispiel.
Frage dazu, weil ich es nicht sicher weiß, aber sehr stark "nein" vermute:
Darf man auf eine globale Integer-Variable (hier: i) von mehreren Threads aus einfach so per inc(i) zugreifen? Ist Inc wirklich atomar, oder müsste das nicht auch abgesichert werden (InterlockedIncrement, AtomicIncrement, CriticalSection, whatever?) |
AW: Delphi einfaches Multithread Beispiel.
Ja, Nein, ist es nicht. :stupid:
Delphi-Quellcode:
im Assembler ist nicht thread-safe, allerdings
INC x
Delphi-Quellcode:
ist es,
LOCK INC x
aber niemand will nur deswegen mit Assembler rumpfuschen, darum ![]() Die Lesezugriffe, ohne zusätzliche Absicherung, sind es damit dann auch. Falls man den Wert aber mehrmals (z.B. der IF dann nochmal) verwenden will, dann sollte man sich den Wert einmal kopieren (nur ein Zugriff und dann unveränderliche Kopie). |
AW: Delphi einfaches Multithread Beispiel.
Eigentlich müsste man den kompletten Code streichen und ersetzen. Kein Anfänger oder überhaupt jemand sollte sich ein beispiel daran nehmen.
Ich empfehle hier zumindestest ![]() |
AW: Delphi einfaches Multithread Beispiel.
Zitat:
Zeigt nur das der oft gebrauchte Rat "versuch es ..." nicht immer gut ist. Gruß K-H |
AW: Delphi einfaches Multithread Beispiel.
Multi-Threading ist in dieser Hinsicht um ein Vielfaches böswilliger als nicht initialisierte Variablen. Es wiegt dich teilweise über mehrere Jahre in Sicherheit und schlägt dann ganz unvermittelt aber erbarmungslos zu. Weil die relevante Code-Änderung dummerweise schon so lange zurückliegt, ist das oft ganz schwer herauszufinden.
|
AW: Delphi einfaches Multithread Beispiel.
Zitat:
![]() |
AW: Delphi einfaches Multithread Beispiel.
Gut, dann lag ich ja richtig.
@Redeemer: danke für den Link. Schau ich mir bei Gelegenheit mal genauer an! :thumb: |
AW: Delphi einfaches Multithread Beispiel.
habe mir nun
![]() Wie kann ich den thread löschen ? |
AW: Delphi einfaches Multithread Beispiel.
@p80286
Ja, es ist schon recht schwer zwei Threads fast zur selben Zeit auf die selbe Speicheradresse zugreifen zu lassen, womit es auch schonmal jahrelang gut gehn "kann", vor allem da in Delphi standardmäßig alle Variablen absichtlich gut ausgerichtet sind. OK, im Prnzip hat der RAM "grundsätzlich" nur Adressleitung, womit es unmöglich wäre genau gleichtzeitig zuzugreifen, aber fangen DualChanel an (zwei Adressleitungen) oder DoubleDataRate (DDR) wo zwei Zugriffe auf einer Leidung liegen (einer bei steigender Flanke und der andere bei Fallender) .......... Und wenn wir es uns jetzt noch einfach machen und annehmen, dass Speicherzugriffe immer genau auf 4 Byte ausgerichtet sind und du einen Integer zwischen zwei Zugriffspunkten positionierst, dann sind es zwei einzelne Zugriffe, die dann noch entsprechen maskiert, verschoben und kombiniert werden. Zitat:
Hier ist die Chance gleich ein Vielfaches höher, das Threads sich dabei überschbeiden. > Wert aus RAM in Register kopieren > hochrechnen > und den Wert wieder zurück in den RAM kopieren Dafür gibt es das LOCK, wo dann ein Speicherbereich für andere Kerne gesperrt wird. Bei MultiThread mit mehreren Kernen mag das noch einfach sein, sowas innerhalb eines Chips/Prozessors zu behandeln. PS: Vergesst nicht, dass früher auch nicht alles in der CPU drin war, sondern alles einzelne Funktionseinheiten/Chips die sich dann noch unterhalten müssten. (in Smartphons, Tablets, Schlepptops und vor allem MiniPCs sind CPU, FPU, NorthBridge, SouthBridge, GPIO, Speichercontroller, ... oft nur noch Eins) Aber schlimmer wird es dann auch noch bei MultiCPU, also mehrere Kerne in mehreren Chips auf vielleicht auch noch mehreren verbundenen Boards. Oder ganz modern, verteilte Rechenleitung über mehreren Rechenzentren. In guten Cloudlösungen kannst Problemlos dein Programm in China laufen lassen und es mittendrin und ohne Unterbrechung nach Russland verschieben, in ein anderes Rechenzentrum deines Anbieters. |
AW: Delphi einfaches Multithread Beispiel.
Task = Thread+
Vielleicht besser erstmal klein anfangen, also direkt Threads. Mit Tasks (Fiber im Windows-Jargon) wird es noch spaßiger, da es Arbeitseinheiten sind, die wiederum in einem/mehreren Threads verarbeitet werden. Man könnte Tasks sogar im Hauptthread ausführen lassen, womit es in der Programmierung wie MultiThread aussähe, aber eigentlich SingleThread wäre. :stupid: |
AW: Delphi einfaches Multithread Beispiel.
@himitsu
Ich hab meine ersten Parallelschritte auf einer Einschubkarte mit T800 gemacht. Da war unser liebstes Wort "Deadlock" der hatte ein Speicherschutzkonzept das einen Anfänger in den Wahnsinn treiben konnte. Einziger Vorteil: du hast gelernt dich zu beschränken. Ich hab nur geflucht nd die Karte nach einem halben Jahr versenkt. Heute zehre ih immer noch von den damaligen Erfahrungen..... Gruß K-H |
AW: Delphi einfaches Multithread Beispiel.
Zitat:
|
AW: Delphi einfaches Multithread Beispiel.
Den Thread würde ich nicht löschen wollen. Ich empfinde ihn als gutes Tutorial, wie man hier gemeinsam zu der Lösung eines Problems kommen könnte, nebst sinnvoller Nebenschauplätze.
Sherlock |
AW: Delphi einfaches Multithread Beispiel.
Ich meine, ich habe in meinem Threadtutorial zwei einfache Beispiele. Eins für die WinAPI und eins für die VCL. Nebenbei kann man damit auch etwas tiefer in die Materie einsteigen.
|
AW: Delphi einfaches Multithread Beispiel.
Delphi-Quellcode:
Du setzt zwingend vorraus, das es ein Form1 gibt. Das ist mindestens leichtsinnig. Falls Du auf eine variable ausserhalb Deines Objektes zugreifen willst solltest Du diese auch offensichtlich übergeben.
procedure TmyThread.Syni;
begin form1.Caption:= inttostr(i); end; procedure TmyThread.Synthreads; var line: integer; begin form1.memo1.clear; for line := Low(threads) to High(threads) do form1.Memo1.Lines.Add(inttostr(threads[line])); end;
Delphi-Quellcode:
Du bist schon weiter oben darauf hingewiesen worden, daß dies eher suboptimal ist.
if ((meinThread[i] <> nil ) and (meinThread[i].Terminated = false)) then meinThread[i].Terminate;
Gruß K-H |
AW: Delphi einfaches Multithread Beispiel.
Link vergessen:
![]() |
AW: Delphi einfaches Multithread Beispiel.
Jo, auch für die Wiederverwendbarkeit vorn Code ist es ungut hier "global" auf Form1 zuzugreifen,
sondern man könnte hier ein TMemo (Memo1) oder besser noch TStrings (Memo1.Lines) reingeben, denn dem kann man auch antere StringListen als Log reingeben. Aber am Besten ist hier ein CallBack zuräck in die Form, wo die Form oder sonstwer dann mit dem LogText machen kann, was er will, ohne dass man an ein TMemo auf einer Form1 gebunden ist. Zitat:
Delphi-Quellcode:
if ... = False then
Wer möchte dafür mit einem rosa Wattebausch gesteinigt werden? Und ratet mal was Terminate macht? Genau, es setzt nur Terminated auf True, also unnötig das nochmal zu prüfen, denn TRUEr als TRUE kann es nicht werden.
Delphi-Quellcode:
Und schon ist die Zeile extrem übersichtlich.
if Assigned(meinThread[i]) then
meinThread[i].Terminate; |
AW: Delphi einfaches Multithread Beispiel.
Guten Abend zusammen. Habe alles nochmal überarbeitet.
Hier das Ergebniss. Erhöhe jetzt i mit atomic increment. Synchronize durch queue ersetzt. Habe von ![]() Kritik erwünscht und Verbesserungsvorschläge !
Delphi-Quellcode:
unit Unit1;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Comctrls, System.ImageList, Vcl.ImgList, Vcl.StdCtrls; type TmyThread = class(TThread) private FMainThreadLog: TStrings; public constructor Create(Alog: Tstrings; DoOnTerminate: TnotifyEvent); procedure Execute; override; procedure OutputMessage(const Amessage: string); end; TForm1 = class(TForm) Button1: TButton; ListBox1: TListBox; procedure HandleTerminate(Sender: Tobject); procedure Button1Click(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; i:integer =0; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var i : integer; begin for i :=0 to 5 do TmyThread.Create(ListBox1.Items, HandleTerminate); end; { TmyThread } constructor TmyThread.Create(Alog: Tstrings; DoOnTerminate: TnotifyEvent); begin inherited Create(false); FMainThreadLog := Alog; FreeOnTerminate := true; OnTerminate := DoOnterminate; end; procedure TmyThread.Execute; var z: integer; begin TmyThread.Queue(nil, procedure begin FMainThreadLog.Add(IntToStr(ThreadID)); end ); for z := 0 to 49 do begin AtomicIncrement(i); sleep(random(50)); end; end; procedure TForm1.HandleTerminate(Sender: Tobject); begin Caption := inttostr(i); end; procedure TmyThread.OutputMessage(const Amessage: string); begin if Assigned(FMainThreadLog) then FMainThreadLog.Add(Amessage); end; end. |
AW: Delphi einfaches Multithread Beispiel.
Das neue Beispiel ist genau so schlimm wie das erste.
Du solltest dir nicht nur ein Video ansehen, sondern am besten in Fachbüchern nachschlagen und lernen, wie man das mit Threads richtig macht. Ich würde an deiner Stelle aber noch viel weiter vorne anfangen - bei den Grundlagen. Das klingt hart und ich will dich nicht verletzen. Aber dieser Code hat nichts im Unterforum Tutorials zu suchen. |
AW: Delphi einfaches Multithread Beispiel.
Das ist von Coderage 2019 übernommen. Dann hat der wohl auch keine Ahnung.
|
AW: Delphi einfaches Multithread Beispiel.
Übernehme keinen Code von irgendwoher - außer aus der DP hier ;)
Und ja da hast du recht. Das Beispiel was du übernommen hast ist wirklich gruselig. |
AW: Delphi einfaches Multithread Beispiel.
Das ist der Kanal von Embacadero Germany.
|
AW: Delphi einfaches Multithread Beispiel.
Ich kann mir nicht vorstellen, dass Embarcaeero Germany solchen Code schreibt.
|
AW: Delphi einfaches Multithread Beispiel.
Zitat:
Ich vermute nur warum das Beispiel nicht gut ist, deshalb würde ich mir von den Wissenden konstruktive Aussagen wünschen ala "das ist nicht gut weil", "besser wäre so", "schau mal dieses Beispiel", "diesen Link". Idealerweise dann auf dem Komplexitäts-Niveau auf dem das ursprüngliche Beispiel war, kein Overkill. Ich hab z.B. nur wenig Ahnung von Threads, sie nur selten benutzt und wenn dann so minimal wie hier. Darum bin ich auf einmal von so Aussagen "Synchronize benutzen ist böse" überrascht. Muss ich das jetzt mit Goto, With und ProcessMessages auf den Dachboden verbannen? Wieso? |
AW: Delphi einfaches Multithread Beispiel.
Zitat:
Delphi-Quellcode:
zurück.
for i:=0 to x do
begin machwas; if i and $f =0 then begin label1.caption:=inttostr(i); processMessages; end; end; Da ist der Vorteil eines Threads auf ein Minimum geschrumpft. Man kann damit allerdings für Demonstrationszwecke die ganze Chose einbremsen aber das sollte dann auch explizit gesagt werden. Gruß K-H |
AW: Delphi einfaches Multithread Beispiel.
Ich verstehe glaub ich was du meinst. Der Thread hält an, während er die GUI über synchronize udated? Und über Postmessage ist das ganze dann mehr entkoppelt? Der Thread setzt sie Message ab und macht weiter. Die GUI fängt bei Gelegenheit die Message ab und verarbeitet sie? Hab ich das so richtig verstanden?
Diese Messages sind doch ein Windows-Ding, oder? Wie macht man das anderswo? Kommen jetzt doch schon die Libraries ins Spiel, wenn man das sauber (aber einfach) machen will? Es muss ja nicht immer das updaten der GUI sein, ein Thread im Sinne Worker-Thread kann ja seine Ergebnisse mitteilen wollen und sagen, dass er auf neue Aufgaben wartet. |
AW: Delphi einfaches Multithread Beispiel.
Zitat:
|
AW: Delphi einfaches Multithread Beispiel.
Zitat:
Aber warum sollte ein Thread mehr als eine Aufgabe ausführen. Mir ist das zu komplex. Wenn Dich das interessiert such mal nach ThreadPool. Und das Tut von Michael ist auch recht hilfreich. Gruß K-H |
AW: Delphi einfaches Multithread Beispiel.
Ich frag da auch nur aus Neugier und gucke gern in diesbezügliche Threads, damit ich schon mal ungefähr was weiß wochach ich googlen muss, wenn ich mal ein konkretes Problem habe.
Scheinbar plant Frank ja da auch was für die ![]() |
AW: Delphi einfaches Multithread Beispiel.
Zitat:
Gruß K-H |
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:26 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz