Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   EOutOfResource + Thread + Bitmap + Assign (https://www.delphipraxis.net/173554-eoutofresource-thread-bitmap-assign.html)

silver-moon-2000 3. Mär 2013 11:30

Delphi-Version: XE

EOutOfResource + Thread + Bitmap + Assign
 
Hallo,

ich wende mich wieder mal mit einem Fehler an Euch, auf dessen Schliche ich nicht komme...

Ich habe einen Thread, der einen "Bild" auf ein Bitmap zeichnen soll.
Dieses Bitmap soll dann in der GUI in einem Image angezeigt werden

Delphi-Quellcode:
type TDisplayHandler = class(TThread)
[...]

constructor TDisplayHandler.Create(_createSuspended : Boolean);
begin
  FBitmap := TBitmap.Create;
[...]
end;


destructor TDisplayHandler.Destroy;
begin
  FreeAndNil(FBitmap);
[...]
end;


procedure TDisplayHandler.DisplayBitmap(_data : TObjectList<TTorqueData>);
//-> wird von einem Timer regelmäßig aufgerufen
begin
  FData := _Data;
  if FIsDone then //Graph wurde fertig berechnet, neuer kann berechnet werden
    FIsDone := False;
end;


procedure TDisplayHandler.SyncEvent;
begin
//  if Assigned(FBitmapChangeEvent) then
//    FBitmapChangeEvent(FBitmap);
//eigentlich so, aber dahinter steht effektiv auch nur ein:
  frmMain.Image.Picture.Assign(FBitmap);
end;

procedure TDisplayHandler.Execute;
begin
  while not Terminated do
  begin
    if not FIsDone then
    begin
      DoCreate;
      Synchronize(SyncEvent);
      FIsDone := True;
    end
    else
      Sleep(20);
  end;
end;

end.
Nach einiger Zeit, jedoch unterschiedlich, mal 10 mal 100 Timeraufrufe später, bricht er ab mit
Code:
Project Torque.exe raised exception class EOutOfResources with message 'Out of system resources'.
Der Cursor hängt dann in der Methode
Delphi-Quellcode:
DoCreate
und zwar dort an der ersten Stelle, an der ich auf das Bitmap zugreifen will

Delphi-Quellcode:
procedure TDisplayTorqueOverTime.DoCreate;
begin
[...]
  clearBitmap(FBitmap);
  drawLine(0, FXAxisPos, FWidth, FXAxisPos, clBlack, 3, FBitmap.Canvas); //hier hängt der Cursor
  drawLine(FYAxisPos, 0, FYAxisPos, FHeight, clBlack, 3, FBitmap.Canvas);

[...]

//mit

procedure clearBitmap(_bitmap : TBitmap);
//-> überzeichnet das Canvas des angegebenen Bitmaps weiß
var t_brush : TBrush;
begin
  if Assigned(_bitmap) then
  begin
    t_brush := Tbrush.Create;
    t_brush.Assign(_bitmap.Canvas.Brush);
    _bitmap.Canvas.Brush.Color := clWhite;
    _bitmap.Canvas.Brush.Style := bsSolid;
    _bitmap.Canvas.FillRect(Rect(0, 0, _bitmap.Width, _bitmap.Height));
    _bitmap.Canvas.Brush.Assign(t_brush);
    t_brush.Free;
  end;
end;

procedure drawLine(_x1, _y1, _x2, _y2 : Integer; _color : TColor; _width : Integer; _canvas : TCanvas);
//-> zeichnet im angegebenen Canvas eine Linie
//-> Pen wird dabei nicht verändert, da er zwischengespeichert wird
var t_pen : TPen;
begin
  if Assigned(_canvas) then
  begin
    t_pen := TPen.Create;
    t_pen.Assign(_canvas.Pen);
    _canvas.Pen.Color := _color;
    _canvas.Pen.Width := _width;
    _canvas.MoveTo(_x1, _y1);
    _canvas.LineTo(_x2, _y2);
    _canvas.Pen.Assign(t_pen);
    t_pen.Free;
  end;
end;
Ich finde meinen Fehler nicht, denn FBitmap wird ja nur einmal erstellt undd er Zugriff auf die GUI wird synchronisiert. Oder mache ich einen kapitalen Denkfehler (wäre nicht das erste Mal)?

Für Hilfe wäre ich wie immer immens dankbar...

Namenloser 3. Mär 2013 12:25

AW: EOutOfResource + Thread + Bitmap + Assign
 
Ich würd zunächst in die DPR an den Anfang
Delphi-Quellcode:
ReportMemoryLeaksOnShutdown := True
schreiben und dann mal die Anwendung mal manuell beenden, bevor die Exception kommt. Dann siehst du (wahrscheinlich) zumindest schon mal, was da eigentlich den Speicher zumüllt. Wo der Cursor stehen bleibt, ist nämlich wenig aussagekräftig, denn das zeigt dir ja nur, wo der Tropfen das Fass zum Überlaufen gebracht hat.

Ansonsten seh ich da jetzt auf den ersten Blick nichts. Ich denke, da ist mehr Code erforderlich, um eine Diagnose zu stellen... z.B. seh ich nirgendwo den Timer, von dem du redest.

Was noch sein könnte, ist, dass innerhalb des Threads eine stille Exception auftritt, wodurch eine Routine frühzeitig abgebrochen wird, bevor ein Objekt freigegeben wird. In solchen Fällen ist Try-Finally wirklich wichtig. Du kannst ja mal einen Breakpoint in den Thread setzen um dir die Exceptions anzeigen zu lassen.

silver-moon-2000 3. Mär 2013 13:36

AW: EOutOfResource + Thread + Bitmap + Assign
 
Danke für Deine Antwort,

Wenn ich
Delphi-Quellcode:
ReportMemoryLeaksOnShutdown := True
verwende, poppt kein Fenster auf, das mich darüber informiert, wie schlecht ich programmiere, also gehe ich davon aus, zumindest keine allzu offensichtlichen Speicherlöcher zu hinterlassen

Seltsam, sobald ich die Synchronize-Methode auskommentiere, läuft das Programm "stundenlang" fehlerfrei:
Delphi-Quellcode:
procedure TDisplayHandler.Execute;
begin
  while not Terminated do
  begin
    if not FIsDone then
    begin
      DoCreate;
     // Synchronize(SyncEvent); //wenn das auskommentiert ist, läuft das Programm
      FIsDone := True;
    end
    else
      Sleep(20);
  end;
end;
Mit anderen Worten: Das Bitmap wird nach wie vor "bemalt", jedoch nicht mehr per Assign in das Image der MainForm "kopiert",
das schließt mMn einen Fehler bei der "Speicherzumüllung" aus.
Also muss mMn ein Fehler bei der Übergabe an die GUI passieren, obwohl der Zugriff darauf synchronized ist? :wiejetzt:

Namenloser 3. Mär 2013 13:45

AW: EOutOfResource + Thread + Bitmap + Assign
 
Ah halt, er sagt ja auch „System Resources“ und nicht „Memory“.

Da fällt mir ein, ich hatte glaube ich vor gar nicht all zu langer Zeit ein ähnliches Problem – ich bin mir nicht mehr 100%ig sicher, aber ich glaube es war so, dass GDI-Objekte immer einem bestimmten Thread angehören. D.h. du kannst sie nicht im einen Thread erzeugen und in einem anderen verwenden, bzw. es kann dann zu Fehlern kommen. Genau das tust du aber, weil der contructor des Threads noch im Hauptthread ausgeführt wird.

Schieb die Erzeugung von FBitmap mal vom constructor in die Execute-Methode (und die Freigabe natürlich auch).


Gerade im fraglichen Projekt, wo der Fehler bei mir auftrat, noch mal nachgeschaut. Vergiss das mit dem constructor. Du musst einfach nur Canvas.Lock/Unlock vor und nach deinen Zeichenoperationen aufrufen. Das war des Rätsels Lösung.

Edit²: Hat zumindest bei mir gereicht.

Bernhard Geyer 3. Mär 2013 13:50

AW: EOutOfResource + Thread + Bitmap + Assign
 
Zitat:

Zitat von NamenLozer (Beitrag 1205774)
... aber ich glaube es war so, dass GDI-Objekte immer einem bestimmten Thread angehören. ...

Genau das ist es. Windows-GDI-Ressourcen sind Thread-Affine. D.h. auf sie darf nur im erzeugenden Thread zugegriffen werden. Ansonsten kracht irgendwo irgendwas.

silver-moon-2000 3. Mär 2013 14:18

AW: EOutOfResource + Thread + Bitmap + Assign
 
Zitat:

Zitat von NamenLozer (Beitrag 1205774)
Gerade im fraglichen Projekt, wo der Fehler bei mir auftrat, noch mal nachgeschaut. Vergiss das mit dem constructor. Du musst einfach nur Canvas.Lock/Unlock vor und nach deinen Zeichenoperationen aufrufen. Das war des Rätsels Lösung.

Edit²: Hat zumindest bei mir gereicht.

Anscheinend nicht nur bei Dir...
Seltsam, seltsam...:gruebel:

Zitat:

Zitat von Bernhard Geyer (Beitrag 1205776)
Zitat:

Zitat von NamenLozer (Beitrag 1205774)
... aber ich glaube es war so, dass GDI-Objekte immer einem bestimmten Thread angehören. ...

Genau das ist es. Windows-GDI-Ressourcen sind Thread-Affine. D.h. auf sie darf nur im erzeugenden Thread zugegriffen werden. Ansonsten kracht irgendwo irgendwas.

OK, wieder was gelernt.

Auf jeden Fall vielen Dank für Eure Antworten,
andere Baustellen rufen mich jedoch , da jetzt diese anscheinend begradigt wurde:dp:


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:35 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