AW: Threads und TBitmaps
Was für Fehler kommen da? Bist du sicher, dass die nicht damit zusammen hängen, dass du da reichlich Speicherlecks produzierst? Oder mit dem Abspeichern?
|
AW: Threads und TBitmaps
Liste der Anhänge anzeigen (Anzahl: 4)
@Medium danke für den Hinweis auf das fehlende Free, aber damit hat es nicht zu tun, wenn du die Bitmaps anschaust siehst Du was ich meine.
|
AW: Threads und TBitmaps
Also auch das Beispiel geht bei mir einwandfrei...
Delphi-Quellcode:
mit 200 Threads probiert (auch wenn des sinnbefreit ist..)
procedure TTestThread.Execute;
var bmp :TBitmap; i:Integer; begin bmp :=TBitmap.Create; bmp.Canvas.Lock; bmp.Width := 100; bmp.Height := 100; bmp.Canvas.Pen.Color := clBlue; for I := 0 to 100 do begin bmp.Canvas.MoveTo(0,0); bmp.Canvas.LineTo(100,i); end; bmp.SaveToFile('C:\temp\test\' + Ffn ); bmp.Free; end; |
AW: Threads und TBitmaps
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
Diese findet allerdings nicht im Threadablauf, sondern im Prozess der Hauptform statt, während das Bild per Synchronize gezeichnet wird. Diese EAccessViolation findet sogar dann statt, wenn man den Code so abändert:
Delphi-Quellcode:
Das bedeutet, dass allein das Aufrufen der (leeren) Synchronize-Methode einen EAccess-Error hervorruft, zumal nichts anderes der Auslöser sein kann.
procedure TPaintThread.Execute;
begin While (Terminated = False) do begin try Synchronize(Zeichnen); finally end; sleep(1); end; end; procedure TPaintThread.Zeichnen; begin // end; Versteht ihr, was da schief läuft? (Der Fehler kommt oft nach einigen Sekunden, manchmal aber auch garnicht) |
AW: Threads und TBitmaps
Ich habe das jetzt nochmal erweitert getestet und festgestellt, dass es nicht an der Snchronize-Methode liegt.
JEDE Kommunikation von MainForm zu Thread per Prozedur kann (nach 10-100 Aufrufen im Mittel) diese Access-Violation beim Durchsteppen auslösen. Was ich mich jetzt frage ist: -Wieso kommt es dazu? -Lässt es sich vermeiden? (Vermutlich nicht) -Hat die Fehlermeldung beim Durchsteppen auch Relevanz beim normalen Ablauf des Programms? :? |
AW: Threads und TBitmaps
Also wir müssen uns hier auf eine korrekte Sprache einigen, denn sonst weiß keiner was wirklich gemeint ist. Beim Thema Multithreading ist das einfach unerlässlich.
Es kommt immer auf den Thread-Kontext an. Wenn du also bei irgendetwas im Bereich Multithreading Probleme hast, dann ist es sehr wichtig, dass du dabei den Thread-Kontext mit angibst, und nicht welche Form da was aufruft. Auch die Methode einer Form kann in jedem beliebigen Thread-Kontext aufgerufen werden, niemand hindert dich daran. Greifst du in irgendeinem Thread-Kontext <> MainThread-Kontext auf die VCL zu, dann kann es rummsen - das ist ja das Gemeine, es rummst nicht immer ;) Den aktuellen Thread-Kontext fragt man ganz einfach mit
Delphi-Quellcode:
ab (bei Delphi 7 war das
TThread.CurrentThread.ThreadID
Delphi-Quellcode:
oder so) und den MainThread-Kontext mit
GetCurrentThreadID
Delphi-Quellcode:
. Sind die beiden Werte unterschiedlich, dann ist man eben nicht im MainThread-Kontext.
MainThreadID
|
AW: Threads und TBitmaps
Zitat:
Meines Wissens führt der Thread alles selbstständig aus, was über seine Routine Execute verbunden ist und kein Synchronize ist. Jedenfalls ruft der Hauptprozess eine leere Methode des Threads auf, was zu einem Fehler führt. (siehe oben!) |
AW: Threads und TBitmaps
Zitat:
Also wer ruft jetzt in welchem Thread-Kontext was auf?
Delphi-Quellcode:
PS
procedure TPaintThread.Execute;
begin While (Terminated = False) do begin try Synchronize( Zeichnen ); finally end; sleep(1); end; end; procedure TPaintThread.Zeichnen; begin // end; Wenn die
Delphi-Quellcode:
Methode im MainThread-Kontext aufgerufen wird (weil du diese Methode direkt aufrufst), dann ist es kein Wunder, dass du da Probleme bekommst, denn vom MainThread-Kontext aus
TPaintThred.Execute
Delphi-Quellcode:
aufrufst, dann kann das zu Problemen führen, wie es auch in der Dokumentation angegeben ist.
Synchronize
|
AW: Threads und TBitmaps
Der normale Mainthread-Context erstellt den Thread(TThread)-von da aus läuft alles von selbst weiter.
Der Thread(TThread) ist in einer separaten Unit. Er(Der Thread) arbeitet seine Execute Methode ab. Dabei ruft er die Synchronize(Zeichnen) Prozedur auf, durch welche der Mainthread-Context die Zeichen-Routine ausführt. Dies führt nach Wiederholungen beim Durchsteppen zu einem EAccess-Violation Fehler. Dieser Fehler passiert, da die Zeichenmethode per Synchronize aufgerufen wird, im Mainthread-Context, wobei die Abbruchstelle am Ende der Zeichenroutine(leer) in der Thread-Unit ist. |
AW: Threads und TBitmaps
Kann ich so nicht nachvollziehen, läuft wie erwartet
EDIT Überwachung des Thread-Kontexts
Delphi-Quellcode:
unit PaintThread;
interface uses Classes; type TPaintThread = class( TThread ) private procedure Zeichnen; protected procedure Execute; override; end; implementation uses SysUtils, Windows; { TPaintThread } procedure TPaintThread.Execute; begin if MainThreadID = GetCurrentThreadId then raise Exception.Create( 'Upps, ich darf nicht im MainThread-Kontext sein!' ); inherited; while not Terminated do begin Synchronize( Zeichnen ); Sleep( 1 ); end; end; procedure TPaintThread.Zeichnen; begin if MainThreadID <> GetCurrentThreadId then raise Exception.Create( 'Upps, ich bin nicht im MainThread-Kontext!' ); end; end.
Delphi-Quellcode:
unit FormMain;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, PaintThread; type TForm1 = class( TForm ) private FPaintThread : TPaintThread; public procedure AfterConstruction; override; procedure BeforeDestruction; override; end; var Form1 : TForm1; implementation {$R *.dfm} { TForm1 } procedure TForm1.AfterConstruction; begin inherited; FPaintThread := TPaintThread.Create( False ); end; procedure TForm1.BeforeDestruction; begin inherited; FPaintThread.Free; end; end. |
AW: Threads und TBitmaps
Dann ist es entweder die Delphi Version, die den Unterschied macht, oder, was wahrscheinlicher ist, das Durchsteppen wurde nicht so durchgeführt, wie es muss, damit der fehler auftaucht.
Zum Nachvollziehen habe ich ein Video gelinkt Es zeigt das Kompilieren, welches am Haltepunkt (Thread-constructor) stoppt, und daraufhin mit F7 gedrückt weiterläuft. PS: Deine Exception wird nicht angezeigt |
AW: Threads und TBitmaps
Zitat:
Und bitte genau den Quelltest und nicht irgendeinen - CopyPaste |
AW: Threads und TBitmaps
Delphi-Quellcode:
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, UThread; type TForm1 = class(TForm) Image1: TImage; Timer1: TTimer; procedure FormCreate(Sender: TObject); procedure Timer1Timer(Sender: TObject); private public { Public-Deklarationen } end; var Form1: TForm1; Paintthread : TPaintThread; i : Integer; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin Paintthread := TPaintThread.create(false); end; procedure TForm1.Timer1Timer(Sender: TObject); begin inc(i); Paintthread.zeichnen; end; end.
Delphi-Quellcode:
unit UThread;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, StdCtrls, ExtCtrls; type Image = TImage; TPaintThread = class(TThread) private Zeichenflaeche: TImage; Original, MyBild : TBitmap; i : integer; protected procedure Execute; override; public procedure zeichnen; constructor Create(CreateSuspended: Boolean); end; implementation constructor TPaintThread.Create(CreateSuspended: Boolean); begin inherited; end; procedure TPaintThread.Execute; begin While (Terminated = False) do begin Synchronize(Zeichnen); sleep(1); end; end; procedure TPaintThread.Zeichnen; begin inc(i); end; end.
Delphi-Quellcode:
program Project1;
uses Forms, Unit1 in 'Unit1.pas' {Form1}, UThread in 'UThread.pas'; {$R *.res} begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end. |
AW: Threads und TBitmaps
Der Timer ist aus.
|
AW: Threads und TBitmaps
Meine Versuche mit dem Timer zeigen, dass der Fehler auftaucht, wenn sich der thread mit sleep(1) aktualisiert, jedoch nicht aufzutreten scheint, wenn er sich mit sleep(1000000) aktualisiert, also nichts tut.
Das deutet darauf hin, dass die beiden threads indirekt miteinander kollidieren, auch wenn sie sich niemals Prozeduren oder deklarierte Variablen teilen. Wie das möglich ist, oder wie sich das mit Synchronize deckt, da hab ich keine Ahnung :evil: |
AW: Threads und TBitmaps
Gibt auch keine Fehler, aber wozu willst du die Methode
Delphi-Quellcode:
überhaupt von aussen aufrufen?
TPaintThread.zeichnen
|
AW: Threads und TBitmaps
Ich denke, ich kann das Problem (oder jedenfalls ein ähnliches) jetzt reproduzieren.
Man nehme einen Timer auf der Form 1 und gebe
Delphi-Quellcode:
ein. Der Haltepunkt sollte darauf liegen.
Paintthread.zeichnen;
Der Thread muss erstellt sein und seine Methode
Delphi-Quellcode:
sein. Jetzt einfach starten und F7 drücken, fertigWhile (Terminated = False) do begin end; |
AW: Threads und TBitmaps
Sollte das bei dir keinen Fehler geben, liegt das Problem wohl am Delphi 7 Debugger.
Neben dem jetzigen Fehler habe ich eben einen bordbk70.dll access violation bekommen, der scheinbar mit Delphi 7 verbunden ist.# Zudem mehrere schwere Fehler beim Debuggen, woraufhin Delphi geschlossen werden musste. Das wäre eine Erklärung für das Desaster hier...Delphi 7 mag einfach noch keine Threads... |
AW: Threads und TBitmaps
Eigentlich schon, aber wohl nicht solche komischen Sachen, die du da machst. ;)
|
AW: Threads und TBitmaps
Ich verwende schrittweises Kompilieren immer zur Fehlersuche.
Wenn dadurch in Verbindung mit Threads aber zusätzliche Fehler entstehen bzw. der Compiler aufhört weiterzuspringen dann ist Fehlersuche kaum möglich. Werde wohl oder übel ohne die Funktion weitermachen müssen ... |
AW: Threads und TBitmaps
Zitat:
Es wird komplett kompiliert und dann schrittweise durch den erzeugten Code gelaufen ;) |
AW: Threads und TBitmaps
Danke für die ganze Hilfe.
Stück für Stück bekomme ich das Endbild nun hin. Das Locken und Unlocken ist noch die größte Herausforderung, denn wenn auch nur 1 lock oder unlock fehlt geht die ganze Anzeige meistens nichtmehr. |
AW: Threads und TBitmaps
Der Timer da macht mir echte Bauchschmerzen. Probier doch mal bitte folgendes aus:
Delphi-Quellcode:
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, UThread; type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure ThreadPaint(var msg: TMessage); message WM_THREADPAINT; private public { Public-Deklarationen } end; var Form1: TForm1; Paintthread : TPaintThread; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin Paintthread := TPaintThread.Create(false, self.Handle); end; procedure TForm1.ThreadPaint(var msg: TMessage); begin Paintthread.LockBMP; try self.Canvas.Draw(40, 40, PaintThread.BMP); finally Paintthread.UnlockBMP; end; end; end.
Delphi-Quellcode:
Edit:
unit UThread;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, StdCtrls, ExtCtrls, SyncObjs; type TPaintThread = class(TThread) private FBMP: TBitmap; FBMPCS: TCriticalSection; FHandle: THandle; protected procedure Execute; override; public constructor Create(CreateSuspended: Boolean; aMainformHandle: THandle); procedure LockBMP; procedure UnlockBMP; property BMP: TBitmap read FBMP; end; const WM_THREADPAINT = WM_USER + 1234; implementation constructor TPaintThread.Create(CreateSuspended: Boolean; aMainformHandle: THandle); begin inherited Create(CreateSuspended); FBMPCS := TCriticalSection.Create; FBMP := TBitmap.Create; FBMP.Width := 300; FBMP.Height := 300; FHandle := aMainformHandle; end; procedure TPaintThread.Execute; begin while not Terminated do begin LockBMP; try FBMP.Canvas.MoveTo(Random(300), Random(300)); FBMP.Canvas.LineTo(Random(300), Random(300)); finally UnlockBMP; end; PostMessage(FHandle, WM_THREADPAINT, 0, 0); sleep(1); end; end; procedure TPaintThread.LockBMP; begin FBMPCS.Enter; FBMP.Canvas.Lock; end; procedure TPaintThread.UnlockBMP; begin FMP.Canvas.Unlock; FBMPCS.Leave; end; end. Zitat:
|
AW: Threads und TBitmaps
Darum nimmt man dafür auch ein
Delphi-Quellcode:
, dann ist das gesichert
try .. finally
Delphi-Quellcode:
Irgendwie scheint dir das aber (warum auch immer) zu missfallen ...
SomeCanvas.Lock;
try // use SomeCanvas finally SomeCanvas.Unlock; end; |
AW: Threads und TBitmaps
@Medium
Die Eigenschaft
Delphi-Quellcode:
direkt auf das Feld zu leiten ist natürlich auch Quell des Übels. Da sollte ein Getter den Zugriff regeln.
BMP
Und warum willst du mit dem Thread auf genau diese Instanz malen? Wieso bekommt der Thread dafür nicht eine temporäre Instanz, erstellt das Bitmap und wenn fertig, wird das einfach per Assign an die Bitmap-Instanz gegeben, die von aussen lesbar ist? Das ist einfach abzusichern und die Blockierung ist minimalst. EDIT Und absolut idiotensicher wird der Zugriff, wenn man für den Zugriff eine Methode baut, die den Inhalt an eine Bitmap-Instanz übergibt (falls ein Oberschlaumeier sich einfach die Instanz merkt). Siehe Beitrag #19 |
AW: Threads und TBitmaps
So wie sich der TE hier bisher gegen "zu viel Arbeit" wehrt, wollte ich ihm keine voll ausgestaltete Profi-Lösung mit Queues und allem vorsetzen. Ich sagte "ausprobieren", nicht das genau so benutzen. Es ging mir erstmal nur darum zu sehen, ob das bei ihm auch knallt. Wenn dir mein Code aber so missfällt, kann ich den Beitrag gerne entfernen.
Übrigens: Ein Getter/Setter der lockt bringt da gar nix, weil dann gerade mal das kopieren der Referenz abgesichert würde. Dann schon mindestens mit einer Kopie, aber zum ausprobieren ob ihm der Debugger auch dabei ins Gesicht springt sollte ein "manuelles" Locken über die CS und den Canvas einfach mal genügen. Ich sehe es auch einfach nicht ein, hier fix und fertige Frameworks zu erstellen. |
AW: Threads und TBitmaps
Ein Getter/Setter der lockt, sichert eben nicht das Kopieren der Referenz ;)
Ok, der Zugriff auf die Referenz ist gesichert, jupp, darum ja auch die Methode aus Beitrag #19 ;) |
AW: Threads und TBitmaps
Das Programm läuft mittlerweile ganz gut, im Vergleich zu vorher hat es nun 0,1 MB/s Datenträgerauslastung.
Seltsam ist, dass nach ca. 30 sek Laufzeit die Datenträgerauslastung vom System von 5% auf 100% hochfährt und dort bleibt, bis das Programm beendet wird. Wahrscheinlich wieder ein Thread Fehler, nicht wahr? :roll: Edit: Entwarnung, zwar passiert das Regelmäßig, doch geht die Auslastung nach kurzer Zeit wieder zurück. |
AW: Threads und TBitmaps
Was soll man dazu sagen ausser: Wenn es so ist, dann ist es so.
Überleg mal, warum ... (Kleiner Tip: Wenn überhaupt arbeitet ein zu vernachlässigbar kleiner Kreis an Personen hier im Forum bei der NSA/irgendeinem Geheimdienst oder als Hellseher - wir können also nicht auf deinen Rechner zugreifen oder deine Gedanken lesen. Wie sollen wir also jetzt etwas zu dem Quelltext sagen, der dieses Verhalten verursacht?) |
AW: Threads und TBitmaps
Jetzt geht's auf ein Mal um MB/s auf der Platte. Ich gebs auf. Dieser Thread ist mittlerweile fast schon eine Parodie. Was hat das noch mit Threads oder Bitmaps zu tun? Aber schön, dass es läuft...
|
AW: Threads und TBitmaps
Hab jetzt auch den halben morgen damit verbracht, TBitmap richtig im Thread zu benutzten.
Fazit: Finger Weg von TBitmap und Threads. Selbst wenn es ohne Zugriffs Verletzungen klappt, alles gekapselt ist, kann es trotzdem überall Knallen, oder der Speicher läuft voll und und und. Am besten direkt TBitmap32 aus Graphics32 nutzen. |
AW: Threads und TBitmaps
Oder einfach auf FMX umsteigen und ab Tokyo sind die Dinger eben doch Thread sicher.
http://docwiki.embarcadero.com/RADSt...and_TContext3D Und ein halber morgen reicht nicht, um VCL-Bitmaps in Threads zu verwenden. Ich hab dazu einen Monat gebraucht... Sherlock |
AW: Threads und TBitmaps
Zitat:
|
AW: Threads und TBitmaps
Nur in FMX!!!
Sherlock |
AW: Threads und TBitmaps
Zitat:
https://github.com/graphics32/graphics32 https://graphics32.github.io/Docs/_Body.htm |
AW: Threads und TBitmaps
Nope - kein Opensource here
|
AW: Threads und TBitmaps
Zitat:
|
AW: Threads und TBitmaps
Kurzer Ausflug ins OT: Meine Begeisterung für OpenSource hält sich in Grenzen. In diesem Fall scheint die Dokumentation halbwegs sauber zu sein, aber mir sind schon Teile untergekommen, wo bereits die Installation ein undokumentiertes Chaos war, von der weiteren Benutzung der "phantastischen" Komponenten mal ganz abgesehen. Sollte das also taveunis Motivation gegen OS sein, ist das für mich absolut nachvollziehbar.
Sherlock |
AW: Threads und TBitmaps
Zitat:
Auch ein wenig OT von mir dazu... Stimme dir zum Open Source Chaos grundsätzlich zu, aber das TGraphics32 Framework stellt für uns eine löbliche Ausnahme dar, wie auch beispielsweise Synapse von Ararat. Hängt natürlich auch immer vom Willen zur Einarbeitung ab. Für mich dient das überblickbare GR32 Framework als Basis für unsere skalierbare Gui, auf die mehr als 32 Threads / Tasks erfolgreich zugreifen. Tja, ich war ursprünglich von FM ziemlich angetan, zumal ich auch von Eugene, dem "Erfinder" die VGScene / GLScene Sources erwarb, bevor er die IP an Emba verkaufte. Hatte eben den Nachteil, dass die (DX) Performance mit jedem Update immer schlechter wurde... (xe2 > xe7). FMX werden wir uns auf absehbare Zeit nicht mehr antun, da gehts dann eher Richtung Vulkan... |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:47 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz