Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Synchronisation in Threads
HenryHux - Do 24.02.11 23:01
Titel: Synchronisation in Threads
Hi, bin immoment auf Fehlersuche, weiß aber echt nicht wo der Fehler liegt.
Ich wollte mal Fragen, was man denn so alles in Threads synchronisieren muss.
Gehört sowas dazu?
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| If CompareMem (Bitmap1.Scanline[Bitmap1.Height - 1], Bitmap2.ScanLine [Bitmap2.Height - 1], Bitmap1.Width*Bitmap1.Height*SizeOf(TRGBTriple)) Then Result := true Else Result := false; |
Oder ImageLists? zb
Lg
jaenicke - Do 24.02.11 23:05
Solange Bitmap1 und Bitmap2 im Thread erzeugt wurden, kann das auch ohne funktionieren. Denn hier greifst du ja nur auf den Speicher zu.
Grundsätzlich ist alles was mit visueller Darstellung zu tun hat, also auch das Zeichnen auf eine Bitmap, potentiell gefährlich in Threads. Spätestens, wenn du in einem Thread eine Bitmap erzeugst und in einem anderen drauf zugreifst, knallt es.
Sinspin - Fr 25.02.11 11:56
Was genau bekommst Du denn für einen Fehler? Eine Schutzverletzung?
Wenn Du den Speicher einer (hier der letzten) Bitmapzeile vergleichen willst dann darfst du als Byteanzahl auch nur die Länge einer Zeile angeben und nicht die des gesammten Bitmaps. Geht es Dir ums ganze Bild brauchst Du Scanline[0] als Zeiger auf die Bitmaps.
Gausi - Fr 25.02.11 12:15
Ich habe die Erfahrung gemacht, dass Bitmaps in Threads generell gefährlich sind. Selbst wenn Bitmaps nur innerhalb eines Threads erstellt, benutzt und wieder zerstört werden, können merkwürdige Dinge passieren.
Woran das genau liegt, weiß ich nicht. :nixweiss:
Sinspin - Fr 25.02.11 12:50
Innerhalb eines Threads hatte ich bisher nie Probleme, aber wenn man ein Bitmap Threadübergreifend verwendete rumpelt es hin und wieder, komischweise selbst mit Synchronisation. Ich arbeite daher immer mit selber reservierten Speicherblöcken. Zum speichern oder laden lege ich mir kurz ein Bitmap an.
jaenicke - Fr 25.02.11 13:32
Sinspin hat folgendes geschrieben : |
| aber wenn man ein Bitmap Threadübergreifend verwendete rumpelt es hin und wieder, komischweise selbst mit Synchronisation. |
Eine Bitmap, auf deren HDC du (mit Canvas z.B.) zugreifst, muss
immer im Hauptthread erstellt werden und darf auch nur dort benutzt werden (synchronisiert eben). Wenn sowohl die Erstellung als auch die Zugriffe synchronisiert passieren, sollte es funktionieren.
Bei Zugriffen mit ScanLine bin ich mir nicht wirklich sicher, würde aber auch davon abraten die im Thread zu versuchen.
bummi - Fr 25.02.11 14:59
Zugriffe aus Threads scheinen sicher zu sein wenn man sie mit Canvas.Lock / canvas.Unlock kapselt, ob das sinnvoll ist mag dahingestellt sein.
HenryHux - Fr 25.02.11 17:22
Ich habe echt keine Idee mehr, wo das Problem liegen könnte.
So sieht bei mir alles aus
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31:
| var i : Integer; comparepicture : TBitmap; TargetBmp : TBitmap; begin result := 0; try SetPositions(links,oben,links,oben, T); ComparePicture:=Tbitmap.create; TargetBmp := TBitmap.Create;
Thread[T].ScreenshotSynched(Rect(Links,Oben,Links+100,Oben+100), 32,43,64,90, TargetBmp); for i := 1 to 20 do begin GUI.CardsImgLst.GetBitmap(i-1, ComparePicture); if CompareImages(TargetBmp, ComparePicture) then begin if (i>0) and (i<20) then begin Result := i; exit(); end; end; end; finally FreeAndNil(TargetBmp); freeandnil(ComparePicture); end; end; |
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46:
| procedure TProcess.Screenshot; var DesktopHandle: HDC; ShotTargetBitmap : TBitmap; begin if not Assigned(SynchP.TargetBmp) then Exit; Shottargetbitmap := TBitmap.Create; try ShotTargetbitmap.Width := SynchP.Bounds.Right - SynchP.Bounds.Left; ShotTargetbitmap.Height := SynchP.Bounds.Bottom - SynchP.Bounds.Top; SynchP.TargetBmp.Width := SynchP.Rechts - SynchP.Links; SynchP.TargetBmp.Height := SynchP.Unten - SynchP.Oben; DesktopHandle := GetDC(0); try BitBlt(ShotTargetBitmap.Canvas.Handle, 0 ,0, SynchP.bounds.Right-SynchP.bounds.left, SynchP.bounds.Bottom - SynchP.bounds.top, DesktopHandle, SynchP.bounds.Left, SynchP.bounds.Top,SRCCOPY); BitBlt(SynchP.TargetBmp.Canvas.Handle, 0, 0, SynchP.Rechts, SynchP.Unten, ShotTargetBitmap.Canvas.Handle, SynchP.Links, SynchP.Oben, SRCCOPY); finally ReleaseDC(0, DesktopHandle); end; finally FreeAndNil(ShotTargetBitmap); end; end;
procedure TProcess.ScreenshotSynched(Bounds2:TRect; Links2, Rechts2, Oben2, Unten2 : Integer; var Bmp : TBitmap); begin try with SynchP do begin TargetBmp := TBitmap.Create; Bounds := Bounds2; Links := Links2; Rechts := Rechts2; Oben := Oben2; Unten := Unten2; end; SYNCHRONIZE(ScreenShot); Bmp.Assign(SynchP.TargetBmp); finally SynchP.TargetBmp.free; end; end; |
Hier ist dann die Funktion ums zu vergleichen
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| If CompareMem (Bitmap1.Scanline[Bitmap1.Height - 1], Bitmap2.ScanLine [Bitmap2.Height - 1], Bitmap1.Width*Bitmap1.Height*SizeOf(TRGBTriple)) Then Result := true Else Result := false; |
Wie gesagt, ein Thread läuft.
Bei 2 läuft er auch, aber alles andere als stabil und fehlerfrei.
Wenn irgendjemandem was spontan beim überfliegen auffällt, kann er es ja gerne schreiben =)
Danke und Liebe Grüße
Gausi - Fr 25.02.11 17:47
Da ist doch das, was jaenicke auch sagte:
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| begin try with SynchP do begin TargetBmp := TBitmap.Create; Bounds := Bounds2; |
Du erzeugst/verwendest im Kontext eines Threads ein Bitmap. Das ist Böhse(tm). :mahn:
HenryHux - Fr 25.02.11 18:22
Ok, geändert.
Doch das dürfte doch nicht das Problem verursachen, das ich suche oder ist da was an mir vorbeigegangen, was man über TBitmaps wissen sollte? :D
Lg
Gausi - Fr 25.02.11 18:35
Doch, das könnte schon sein. Ich hatte auch mal ein Programm, das in einem Thread ein Bitmap bemalt und das dann synchronisiert in ein Image gepackt hat. Das Ding lief prima - solange man die Maus nicht bewegt hat. ;-)
Bitmap aus dem Thread raus, und das Problem war erledigt. :D
HenryHux - Fr 25.02.11 18:44
Nein, leider war es das nicht :(
Ich werde also weitersuchen.
Lg
jaenicke - Fr 25.02.11 19:00
Das Problem liegt wohl vor allem hier:
HenryHux hat folgendes geschrieben : |
Delphi-Quelltext 1:
| GUI.CardsImgLst.GetBitmap(i-1, ComparePicture); | |
Du greifst hier offenbar nicht nur auf eine ImageList zu, sondern das auch noch unsynchronisiert. Das kann nicht gutgehen.
Denn GetBitmap führt sogar Zeichenoperationen aus soweit ich mich erinnere.
Ich hätte da eine andere Lösung für dich:
Lagere die Prüfung nicht nur in einen eigenen Thread, sondern in einen anderen Prozess aus. Via IPC bekommst du dann das Ergebnis mitgeteilt und gut ist es. Dann sind die ganzen Synchronisierungsprobleme vom Tisch.
HenryHux - Fr 25.02.11 20:06
Ha, das hab ich mir irgendwie schon die ganze Zeit gedacht :D
Habe es so gemacht, mit IPC kenn ich mich kaum aus:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| procedure TProcess.GetMiscImgLstSynched(pos : integer; var bmp : TBitmap); begin MiscL.Nr := Pos; Synchronize(GetMiscImgLst); bmp.Assign(MiscL.Bmp); end;
procedure TProcess.GetMiscImgLst; begin GUI.MiscImgLst.GetBitmap(MiscL.Nr, MiscL.Bmp); end; |
Dürfte das so klappen?
Lg
jaenicke - Fr 25.02.11 21:19
Wenn die Bitmaps alle im Hauptthread erzeugt werden, kann das so gehen, ja. Dann stellt sich aber die Frage was dir der Thread bringt, wenn als Zugriffe eh synchronisiert werden.
Deshalb rate ich wirklich zu einem neuen Prozess, der dann wirklich alles machen kann und dennoch vollkommen parallel laufen kann.
HenryHux - Sa 26.02.11 01:12
Du hast ja IPC angesprochen.
Wie kann ich denn damit sowas realisieren?
Mir würde nur einfallen, einen Thread zu erzeugen, auf welchen dann gewartet werden müsste, denn ohne das Ergebnis gehts ja nicht weiter. Als Schlagwörter würden mir da Pipes und MemoryMapping einfallen, aber wie gesagt ich weiß nicht wie man mit sowas effektiv umgeht.
Lg
jaenicke - Sa 26.02.11 03:35
Wenn es nur um einen Integerwert als Rückgabewert geht, kannst du auch einfach den ExitCode in dem neuen Prozess zuweisen und im alten mit CreateProcess arbeiten und im Thread mit WaitForMultipleObjects auf die Beendigung warten.
Danach kannst du den ExitCode des Prozesses mit GetExitCodeProcess abfragen.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!