Przekazywanie obiektu (klasy) stworzonego w wątku do wątku głównego.

0

Napisałem sobie pewien programik, który wątkowo oblicza pewne rzeczy.

Stworzyłem sobie nowy typ np.:

 PResult = ^TResult;
 TResult = record
    Value:int64;
    Divides:int64;
    Number:int64;
    Item:integer;
  end;

Następnie w wątku alokuję sobie pamięć dla tego typu i przekazuję wynik do wątku głównego w następujący sposób:

procedure TDThread.Execute;
var Resultt:PResult;
begin
...
  new(Resultt);
  Resultt^.Value:=a;
  Resultt^.Divides:=b;
  Resultt^.Number:=c;
  Resultt^.Item:=d;
  PostMessage(FormHandle,WM_NEWRESULT,integer(Resultt),e);
...
end;

Na formie mam zdefiniowaną obsługę komunikatu WM_NEWRESULT gdzie następuje odczyt przekazanych danych i zwolnienie pamięci:

procedure TForm7.WMNewResult(var Msg: TMessage);
var  Results:PResult;
begin
  Results:=pointer(Msg.WParam);
...
  Dispose(Results);
end;

Chciałbym w podobny sposób zrobić coś takiego by wątek np. wygenerował jakiś obrazek np. na Canvasie klasy TBitmap i w jakiś sposób go przekazać do wątku głównego. Niestety klasy nie mogą znajdować się w rekordach.

Jedyny pomysł jaki mi przyszedł do głowy to zapisywanie obrazka z TBitmap do pliku i przekazywanie przez wpis w rekordzie stringa z nazwą pliku, gdzie w obsłudze komunikatu następowałby odczyt z utworzonego pliku. Pewnym jest jednak, że byłaby to zbyt powolna metoda.

Pozdrawiam.

0

A myślałeś nad przekazaniem wskaźnika do konstruktora wątku i na nim operowanie?

Po skończeniu po prostu zakończ wątek i tyle; Nie będziesz musiał alokować pamięci w wątku, a dealokować w formularzu (czyli de facto w dwóch różnych miejscach przez dwie osobne klasy).

0
furious programming napisał(a):

A myślałeś nad przekazaniem wskaźnika do konstruktora wątku i na nim operowanie?

Tak wiem o tym, że tak można robić o ile w czasie trwania pracy wątku, wątek główny nie będzie chciał czegoś z tą klasą - obrazkiem zrobić.

Chodzi mi dokładniej o zrobienie ciągłego generowania obrazka - coś na sposób wizualizacji, gdyż wątek działałby w pętli aż do zatrzymania metodą Terminate, zmieniając zawartość obrazka trzymanego przez wątek. Co ileś iteracji np. robiona byłaby kopia obrazka i miałby być wysyłany komunikat funkcją PostMessage do wątku głównego, który odbiera obrazek i go wyświetla na formie i zwalnia pamięć zarezerwowaną na kopię.

0

Niezbyt rozumiem co chcesz zrobić... Chcesz ten obrazek z wątku pobocznego po kawałku wysyłać do głównego, który po kawałku będzie go gdzieś odrysowywał?

0

Chodzi o coś na styl animacji.

W wątku pobocznym wyglądać miałoby to tak:

  • uruchamiam i w tym wątku tworzę klasę TBitmap
  • następnie następuje narysowanie na Canvasie tego TBitmap wstępnych kształtów na podstawie zadanych wartości przy tworzeniu wątku
  • w pętli przekształcam cały obrazek i co n-tą iterację chcę go przekazać do wątku głównego poprzez alokację pamięci dla kopii tego obrazka, skopiowanie go, a następnie za pomocą PostMessage wysłać informację że kopia obrazka jest gotowa do odebrania

W wątku głównym:

  • odbiór komunikatu, że kopia jest gotowa, uzyskanie adresu obrazka i wyświetlenie go na canvasie formy
  • dealokacja pamięci przydzielonej na kopię

Chodzi o to, żeby wątek generujący nie czekał na to, że wątek główny odbierze komunikat tylko natychmiast generował kolejny. Ważne jest też, żeby pamiętało tak jakby wszystkie klatki - z tego powodu dla każdej kopii alokowana jest nowa pamięć, która zwalniana jest po odbiorze.

Mam nadzieję, że jest to zrozumiałe. Chcę przekazywać w podobny sposób obrazek jak wartości zawarte w rekordzie TResult z pierwszego postu.

3

Po pierwsze jako parametr komunikatu możesz pokazać wskaźnik na obiekt, po drugie dla głupiego obrazka wystarczy że przekażesz jego uchwyt czyli np. dla bitmapy czyli Bitmap.Handle.

EDIT//
Łap przykłady z przekazaniem uchwytu:

  PResult = ^TResult;
  TResult = record
    Value:int64;
    Divides:int64;
    Number:int64;
    Item:integer;
    hBmp: HBITMAP;
  end;
//...
procedure TForm1.WMNewResult(var Msg: TMessage);
var
  R: TResult;
begin
  R:= TResult(PResult(Msg.WParam)^);
  Image2.Picture.Bitmap.Handle:= R.hBmp; //laduje odebrana bitmape do Image2
   Msg.Result:= 1;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  R: TResult;
begin
  R.hBmp:= Image1.Picture.Bitmap.Handle; //w image1 mam zaladowana bitmape
  SendMessage(Handle, NEW_RESULT, Integer(@R), 0);
end;

i przekazaniem obiektu (w przykładzie TBitmap):

  PResult = ^TResult;
  TResult = record
    Value:int64;
    Divides:int64;
    Number:int64;
    Item:integer;
    Bmp: TBitmap;
  end;
//....
procedure TForm1.WMNewResult(var Msg: TMessage);
var
  R: TResult;
begin
  R:= TResult(PResult(Msg.WParam)^);
  Image2.Picture.Bitmap.Assign(R.Bmp); //laduje odebrana bitmape do Image2
  Msg.Result:= 1;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  R: TResult;
begin
  R.Bmp:= Image1.Picture.Bitmap; //w image1 mam zaladowana bitmape
  SendMessage(Handle, NEW_RESULT, Integer(@R), 0);
end;

A i wysyłaj SendMessage a nie PostMessage jeżeli chcesz aby kod po wysłaniu komunikatu wykonał się dopiero wtedy jak komunikat zostanie obsłużony.

0

Nie bardzo kumam co się stało ale zadziałało coś co czego wcześniej mi nie chciało akceptować.
Po prostu zadziałało to tak:

  • dodałem do rekordu TResult wpis Bitmap:TBitmap;
  • w procedurze wątku dodałem:
var BTMap:TBitmap;
BTMap:=TBitmap.Create(20,20);
Resultt^.Bitmap:=BTMap;
  • a w obsłudze komunikatu zwolniłem obrazek przez:
Results^.Bitmap.Free;

Także, póki co działa na pustym obrazie. Zobaczymy co i jak dalej.

PS dzieki za przykład, zerknę na niego.

0

Jest jednak problem gdyż nie każdy generowany obrazek jest wyświetlany na formie a najczęściej pierwszy, czasem też nieprawidłowo.
W wątku pobocznym mam przykładowo:

GBitmap:=TBitmap.Create;
GBitmap.SetSize(20,20);
GBitmap.Canvas.brush.Color:=(random(256)*256+random(256))*256+random(256);
GBitmap.Canvas.FillRect(TRect.Create(5,5,15,15));
GBitmap.Canvas.pen.Color:=(random(256)*256+random(256))*256+random(256);
GBitmap.Canvas.Pen.Width:=3;
GBitmap.Canvas.MoveTo(random(20),random(20));
GBitmap.Canvas.LineTo(random(20),random(20));
GBitmap.Canvas.LineTo(random(20),random(20));
GBitmap.Canvas.LineTo(random(20),random(20));
GBitmap.Canvas.LineTo(random(20),random(20));
GBitmap.Canvas.LineTo(random(20),random(20));
Resultt^.Bitmap:=GBitmap;
PostMessage(FormHandle,WM_NEWRESULT,integer(Resultt),FID);
PostMessage(FormHandle,WM_UPDATEPROGRESS,FID,Count);
sleep(1000) // dla zauważenia efektów

W obsłudze zdarzenia WM_NEWRESULT mam:

  Results:=pointer(Msg.WParam);
Image1.Picture.Bitmap.Assign(Results^.Bitmap);
Results^.Bitmap.Free;
Dispose(Results);
Msg.Result:=1;

Obsługa zdarzenia WM_UPDATEPROGRESS:

procedure TForm7.UpdateProgress(var Msg: TMessage);
begin
  ProgressBar1.Position:=Msg.LParam;
end;

Zauważyłem, że gdy aktualizuje się ProgressBar obrazki nie są wyświetlane lub są nieprawidłowo.
Usunięcie tego wysłania komunikatu aktualizacji ProgressBar pozostawia jedynie nienarysowany pierwszy obrazek.
Sprawdziłem obie opcje: SendMessage i PostMessage - bez różnic.
Co może być przyczyną takiego zjawiska?

Edit:
Na pewno ma to związek aktualizacją ProgressBara - w momencie gdy jego zawartość wypełnia się. Gdy go nie aktualizuje to jedynie problem jest z pierwszym obrazem.

Dodatkowo można zasymulować to zjawisko zmieniając rozmiar okienka, wtedy tak samo błędy (kolory, cienkie linie) są w generowaniu obrazków przez wątek poboczny. Tak jakby część procedur na Canvasie nie została wykonanych - pewnie klasa Canvas nie może być używana w wątku pobocznym i działa prawidłowo tylko w głównym.

0

Nie chce mi się już sprawdzać ale ja jednak spróbował bym użyć SendMessage ale w Synchronize chociaż to dość dziwna konstrukcja ale spróbować można.

EDIT//
Albo może tam w obsłudze komunikatu jakieś po Assign daj jakieś Application.ProcessMessages oczywiście po mojemu do tego celu powinno być SendMessage przecież główny wątek musi narysować bitmapę w ten sposób będzie zagwarantowane że wątek poboczny na to zaczeka... niby masz tam sleepa ale wiesz po co stosować półśrodki jak można to zrobić po ludzku.

0

Wstępne rozwiązanie, które póki co działa to potrzeba użycia Canvas.Lock przed operacjami i po zakończeniu Canvas.Unlock.

1 użytkowników online, w tym zalogowanych: 0, gości: 1