Entwickler-Ecke

Windows API - Rechenintensiver Thread friert MainThread (GUI) ein.


kalypso1977 - Di 20.10.09 13:30
Titel: Rechenintensiver Thread friert MainThread (GUI) ein.
Hallo zusammen!

Ich habe ein kleines Demoprogramm geschrieben welches eine rechentintensive Schleife in einem Nebenthread abarbeitet und eine Listbox mit Werten befüllt.

Auf das Fertigstellen der Schleifenabarbeitung im HauptThread (MainForm) soll mit einem MsgWaitForMultipleObjects gewartet werden. Geht auch soweit, aber während der Abarbeitung der rechenintensiven Schleife im NebenThread, soll ein animiertes Gif (loading.gif) weiterlaufen bzw. die GUI des HauptThreads von dieser rechenintensiven Abarbeitung nicht beeinträchtigt werden. Leider bekomme ich das einfach nicht hin. Auch das tolle Thread Tutorial von Michael Puff hat mir nicht wirklich weitergeholfen, von einer Recherche in Google ganz abgesehen.

Kann mir jemand helfen? Ich wäre euch mehr als dankbar. Mein Ziel ist es, einfach gewisse rechenintensive Abläufe in einem Thread zu verlagern die aber die weiterarbeit im Hauptthread solange "stoppen" bis sie sich terminiert haben bzw. abgearbeitet sind...

Anbei der aktuelle Beispiel-Code:


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:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
unit main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, GIFImage, ExtCtrls, StdCtrls, Buttons, ShellApi;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Image1: TImage;
    Button2: TButton;
    ListBox1: TListBox;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function Thread(p: Pointer): Integer;
var
i : integer;
begin
  for i := 0 to 999 do
  begin
    Form1.ListBox1.Items.Add(IntToStr(i));
    sleep(10);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  hThread : THandle;
  ThreadID : Cardinal;
  WaitResult : DWORD;
  Msg : TMsg;
  FGifImage:TGIFImage;

begin

  try

    //animiertes Gif laden und laufen lassen im HauptThread
    FGifImage := TGIFImage.Create;
    FGifImage.LoadFromFile(ExtractFilePath(ParamStr(0))+'loading.gif');
    Image1.Picture.Assign(FGifImage);

    //Rechenintensive Schleife in Thread verlagern und warten bis sich dieser
    //beendet, in dieser Zeit soll das animierte Gif und die
    //HauptForm weiter sauber reagieren...
    hThread := BeginThread(nil0, @Thread, nil0, ThreadID);

    repeat
      WaitResult := MsgWaitForMultipleObjects(1, hThread, False, INFINITE, QS_ALLINPUT);
      if WaitResult = WAIT_OBJECT_0 + 1 then begin
        while PeekMessage(Msg, Form1.Handle, 00, PM_REMOVE) do
        begin
          TranslateMessage(Msg);
          DispatchMessage(Msg);
        end;
      end;
    until WaitResult = WAIT_OBJECT_0;

  finally
    FreeAndNil(FGifImage);
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Application.Terminate;
end;

end.


Moderiert von user profile iconGausi: Code- durch Delphi-Tags ersetzt


Narses - Di 20.10.09 13:49

Moin und :welcome: im Forum!

Ich halte deinen APM-Schleifen-Ansatz für nicht so gut. :nixweiss: Schau mal hier [http://www.delphi-library.de/topic_auf+Threads+in+Formularanwendungen+warten+AppState+T2_76682.html], vielleicht ist das das, was du suchst. :les: :think: ;)

cu
Narses


kalypso1977 - Di 20.10.09 14:23

Hi!

Danke für die schnelle Antwort. Ich habe dein Beispiel mal ausprobiert.

1) Allerdings möchte ich schon, wie in meinem Code, "nach" der Abarbeitung des Threads einfach mit dem MainThread (ButtonCode) weitermachen. Das geht schon mal nicht in deinem Bespiel. Klickt man auf den Startbutton wird dieser vollständig ausgeführt und beendet.

2) Desweiteren wird im TimerThread eine Schleife durchlaufen die entweder terminiert durch einen Timeout oder durch die Anzahl an steps (Steps wird mit Decrease heruntergezählt). Der "Code" innerhalb der Schleife wird also mehrmals ausgeführt. Das möchte ich ja auch nicht. Mein Threadcode soll einmal ausgeführt werden. Er ist halt nur rechenintensiv! Die Schleife in meinem Nebenthread die eine Listbox mit Werten füllt war nur ein Beispiel dafür...

Das Beispiel ist prinzipiell gut, passt aber nicht auf meine Anforderung.

LEIDER!!

Hast du noch eine andere Idee? Bzw. habt Ihr noch eine andere Idee...??


Narses - Di 20.10.09 14:29

Moin!

user profile iconkalypso1977 hat folgendes geschrieben Zum zitierten Posting springen:
Das Beispiel ist prinzipiell gut, passt aber nicht auf meine Anforderung.
Was hast du erwartet, c&p? :zwinker: Der Ansatz ist das Entscheidende::idea: ;)

cu
Narses


kalypso1977 - Di 20.10.09 15:24

Danke für deine Hilfe!

Habs jetzt hinbekommen - ging doch :lol:

Prima!


BenBE - Di 27.10.09 14:58

Der Source im Thread sollte nicht auf die VCL zugreifen ... zumindest nicht unsynchronisiert.

Und dann deaktiviert man bitte schön das automatische Update ... sonst ist das lahm ...