AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Sonstige Fragen zu Delphi Delphi Thread sauber Beenden ? Handle ungültig.
Thema durchsuchen
Ansicht
Themen-Optionen

Thread sauber Beenden ? Handle ungültig.

Ein Thema von El.Blindo · begonnen am 29. Jan 2008 · letzter Beitrag vom 17. Mär 2008
Antwort Antwort
Seite 1 von 2  1 2      
El.Blindo

Registriert seit: 24. Okt 2006
18 Beiträge
 
#1

Thread sauber Beenden ? Handle ungültig.

  Alt 29. Jan 2008, 19:07
Nächster Tag, nächste dumme Frage.

Ich bastel zur Zeit an einem Programm das mit diverse Threads arbeitet, in diesem Beispiel-Thread wird unter anderem ein DLL Funktion importiert, ferner hole ich mir ein Handel im Zusammenhang mit der PDH.DLL.

Dieser Thread läuft in einer Endlos-Schleife (While not Terminated.....).

Nun würde ich gerne beim Beenden des Hauptprogrammes, auch diesen Thread sauber beenden, d.h. meine DLL sowie mein Handle wieder freigeben.

Hier die Thread Unit:

Delphi-Quellcode:
unit PDHThread;

interface

uses
  Classes, Windows, SysUtils;

const
  PDH_NO_DATA = $800007D5;
  PDH_MEMORY_ALLOCATION_FAILURE = $C0000BBB;
  PDH_INVALID_HANDLE = $C0000BBC;
  PDH_INVALID_ARGUMENT = $C0000BBD;

  PDH_FMT_RAW = $00000010;
  PDH_FMT_ANSI = $00000020;
  PDH_FMT_UNICODE = $00000040;
  PDH_FMT_LONG = $00000100;
  PDH_FMT_DOUBLE = $00000200;
  PDH_FMT_LARGE = $00000400;
  PDH_FMT_NOSCALE = $00001000;
  PDH_FMT_1000 = $00002000;
  PDH_FMT_NODATA = $00004000;
  PDH_FMT_NOCAP100 = $00008000;

type
  PQUERY = ^HQUERY;
  HQUERY = THandle;

  PCOUNTER = ^HCOUNTER;
  HCOUNTER = THandle;

  PDH_STATUS = Longint;

  PPDH_FMT_COUNTERVALUE = ^TPDH_FMT_COUNTERVALUE;
  _PDH_FMT_COUNTERVALUE = record
    CStatus : DWORD;
    longValue : Longint;
    doubleValue : double;
    largeValue : LONGLONG;
    AnsiStringValue : LPCSTR;
    WideStringValue : LPCWSTR;
  end;
  TPDH_FMT_COUNTERVALUE = _PDH_FMT_COUNTERVALUE;
  PDH_FMT_COUNTERVALUE = _PDH_FMT_COUNTERVALUE;

type
  TPDHInfo = record
    Counter : HCounter;
    PDHLoad : PDH_FMT_COUNTERVALUE;
  end;

type
  TPDHThread = class(TThread)
  private
    HQ : HQuery;
    PDHInfo : Array[1..4] of TPDHInfo;
    hPDH : THandle;
    procedure UpdatePDH;
  protected
    procedure Execute; override;
  public
  end;

Var
  PdhOpenQuery : function(pReserved: Pointer; dwUserData: DWORD; phQuery: PQUERY): PDH_STATUS; stdcall;
  PdhCloseQuery : function(ahQuery: HQUERY): PDH_STATUS; stdcall;
  PdhAddCounter : function(ahQuery: HQUERY; szFullCounterPath: PChar; dwUserData: DWORD; phCounter: PCOUNTER ): PDH_STATUS; stdcall;
  PdhRemoveCounter : function( ahCounter: HCOUNTER ): PDH_STATUS; stdcall;
  PdhCollectQueryData : function( ahQuery: HQUERY ): PDH_STATUS; stdcall;
  PdhValidatePath : function( szFullCounterPath: PChar ): PDH_STATUS; stdcall;
  PdhGetFormattedCounterValue : function( ahCounter: HCOUNTER; dwFormat: DWORD; lpdwType: LPDWORD; pValue: PPDH_FMT_COUNTERVALUE): PDH_STATUS; stdcall;

implementation

uses Main;

function GetNumberOfProcessors: Integer;
var
  SystemInfo: TSystemInfo;
begin
  GetSystemInfo(SystemInfo);
  Result:=SystemInfo.dwNumberOfProcessors;
end;

Function LoadPDH : THandle;
Var
  H : THandle;
Begin
  H := LoadLibrary('PDH.DLL');
  If H <> 0 then
  Begin
    PdhOpenQuery := GetProcAddress(H, 'PdhOpenQuery');
    PdhCloseQuery := GetProcAddress(H, 'PdhCloseQuery');
    PdhAddCounter := GetProcAddress(H, 'PdhAddCounterA');
    PdhRemoveCounter := GetProcAddress(H, 'PdhRemoveCounter');
    PdhCollectQueryData := GetProcAddress(H, 'PdhCollectQueryData');
    PdhValidatePath := GetProcAddress(H, 'PdhValidatePath');
    PdhGetFormattedCounterValue := GetProcAddress(H, 'PdhGetFormattedCounterValue');
  End;
  Result := H;
End;

procedure TPDHThread.Execute;
var
  X, MaxX : Integer;
  dwctrType : DWord;
begin
  hPDH := LoadPDH;
  If hPDH <> 0 then
  Begin
    If PDHOpenQuery(nil, 1 ,@HQ) = ERROR_SUCCESS then
    Begin
      MaxX := GetNumberOfProcessors;
      If MaxX > 4 then
        MaxX := 4;
      For X := 1 to MaxX do
        If PDHAddCounter(HQ, PChar('\Prozessor('+InttoStr(X-1)+')\Prozessorzeit (%)'), 1, @PDHInfo[X].Counter) <> ERROR_SUCCESS then
          PDHAddCounter(HQ, PChar('\Processor('+InttoStr(X-1)+')\% Processor Time'), 1, @PDHInfo[X].Counter);
      while not Terminated do
      Begin
        If PDHCollectQueryData(HQ) = ERROR_SUCCESS then
          For X := 1 to MaxX do
            PdhGetFormattedCounterValue(PDHInfo[X].Counter, PDH_FMT_DOUBLE, @dwctrType, @PDHInfo[X].PDHLoad);
        Synchronize(UpdatePDH);
        Sleep(1000);
      End;
    PdhCloseQuery(HQ);
    FreeLibrary(hPDH);
    End;
  End;
end;

procedure TPDHThread.UpdatePDH;
begin
  MainForm.UpdatePDH(PDHInfo);
end;

end.
Dieser Thread wird im Hauptprogramm folgendermaßen gestartet:

Delphi-Quellcode:
  // Thread zur Berechnung des CPULoad starten
  PDHThread := TPDHThread.Create(True);
  PDHThread.Priority := tpNormal;
  PDHThread.FreeOnTerminate := True;
  PDHThread.Resume;
Beim Testen des Ganzen bin ich jedoch darauf gestoßen das weder PdhCloseQuery noch FreeLibrary ausgeführt werden.

Also hab ich das Ganze geändert, so:

Delphi-Quellcode:
  // Thread zur Berechnung des CPULoad starten
  PDHThread := TPDHThread.Create(True);
  PDHThread.Priority := tpNormal;
  PDHThread.FreeOnTerminate := False;
  PDHThread.Resume;
Und habe im OnDestroy Event (wahlweise auch im OnCloseQuery) des Hauptprogrammes folgendes eingeführt:

Delphi-Quellcode:
  // Thread zur Berechnung des CPULoad beenden
  PDHThread.Terminate;
  PDHThread.Waitfor;
  PDHThread.Free;
Nun werden PdhCloseQuery und FreeLibrary ausgeführt, allerdings fange ich mir Exceptions.

Handle ist ungültig(6) oder Read of Address 0001.

Wo liegt mein Fehler ?

Wenn ich FreeOnterminate auf True setze ist es für mich noch verständlich, offensichtlich wird der Thread abgewürgt bevor PdhCloseQuery und FreeLibrary ausgeführt werden.

Bei FreeOnterminate auf False, wenn ich den Thread also selber beende, müssten doch die Handles auf hPDH und HQ weiter gültig sein, zumindestens so lange bis ich sie selber frei gebe, oder PDHThread.Free aufrufe. ODER ?

MfG

El.Blindo
  Mit Zitat antworten Zitat
messie

Registriert seit: 2. Mär 2005
Ort: Göttingen
1.592 Beiträge
 
Delphi 2009 Professional
 
#2

Re: Thread sauber Beenden ? Handle ungültig.

  Alt 29. Jan 2008, 19:43
Ich hab' nicht genau durchschaut, was Du da tust, mir ist aber etwas aufgefallen.
Im Thread deklarierst Du
Delphi-Quellcode:
private
    PDHInfo : Array[1..4] of TPDHInfo;
Im execute rufst DuMainForm.UpdatePDH(PDHInfo); auf. Das finde ich etwas komisch. Bei der Variante FreeOnTerminate machst Du PDHInfo platt, obwohl Du evtl. noch im synchronize() steckst, das ist riskant. Ob das auch ohne FreeOnTerminate so ist, kann ich nicht überblicken, aber generell: eine private-Variable eines nachgeordneten Thread an den Hauptthread zu übergeben ist mir unheimlich.
Warum deklarierst Du PDHInfo nicht public im HauptThread?

Den Aufruf von PDHCloseQuery etc. bekommst Du meiner Meinung nach hin, indem Du die while not-Schleife in eine try..finally-Struktur packst - jedenfalls mache ich das so. Dann kannst Du im finally reproduzierbar aufräumen.

Grüße, Messie
  Mit Zitat antworten Zitat
El.Blindo

Registriert seit: 24. Okt 2006
18 Beiträge
 
#3

Re: Thread sauber Beenden ? Handle ungültig.

  Alt 29. Jan 2008, 21:04
Danke, hast mir schon mal sehr geholfen.

Das Problem war das ich auch die Handles im Public erstellt habe und daher auch zu früh platt gemacht habe.

Daran gedacht, die PDHInfo im Hauptprogramm Public zu erstellen habe ich auch schon, mir ist allerdings nicht klar wie ich dann von beiden Threads auf diese Variable zugreifen kann, ohne das es Probleme gibt.

Also wie ich das dann Synchronisized hin bekomme.

MfG

El.Blindo
  Mit Zitat antworten Zitat
Benutzerbild von Dani
Dani

Registriert seit: 19. Jan 2003
732 Beiträge
 
Turbo Delphi für Win32
 
#4

Re: Thread sauber Beenden ? Handle ungültig.

  Alt 29. Jan 2008, 21:26
Hi, so gäbe es etwas weniger Probleme mit der Sichtbarkeit usw... ist aber auch noch nicht so toll, weil sehr speziell.
Delphi-Quellcode:
unit PDHThread;

interface

  {...}

  TPDHThreadEvent = class(TObject)
  private
    FUsage: Double;
    FCPUIndex: Integer;
  public
    constructor Create(Info: TPDHInfo; CPUIndex: Integer);
  published
    property Usage: Double read FUsage write FUsage;
    property CPUIndex: Integer read FCPUIndex write FCPUIndex;
  end;

  {...}

implementation

  {...}

procedure TPDHThread.UpdatePDH;
var I: Integer;
    Event: TPDHThreadEvent;
begin
  for I := Low(PDHInfo) to High(PDHInfo) do begin
    try
      Event := TPDHThreadEvent.Create(PDHInfo[i], i);
      MainForm.UpdatePDH(Event);
    finally
      Event.Free;
    end;
  end;
end;

{ TPDHThreadEvent }

constructor TPDHThreadEvent.Create(Info: TPDHInfo; CPUIndex: Integer);
begin
  inherited Create;
  FUsage := Info.PDHLoad.doubleValue;
  FCPUIndex := CPUIndex;
end;

end.
Dani H.
At Least I Can Say I Tried
  Mit Zitat antworten Zitat
alzaimar
(Moderator)

Registriert seit: 6. Mai 2005
Ort: Berlin
4.956 Beiträge
 
Delphi 2007 Enterprise
 
#5

Re: Thread sauber Beenden ? Handle ungültig.

  Alt 29. Jan 2008, 21:36
Der Fehler ('Das Handle ist ungültig') tritt bei Threads bekanntermaßen dann auf, wenn die Classes-Unit finalisiert wurde und DANACH noch Threads freigegeben werden. TThread.Destroy verwenden Objekte aus der Classes-Unit.

Aber das scheint bei Dir nicht der Fall zu sein. step doch einfach mal den Thread durch (NICHT das Synchronize, das macht keinen Spass).

Ich meine, das Dein Code sauber ist, vor allen Dingen mit der manuellen Freigabe.

Erstelle Dir ein kleines Projekt mit zwei Buttons.
Button1 = Thread-Instantiierung und -Start
Button2 = Thread-Ende und Destroy.

Klappt das dann auch?
"Wenn ist das Nunstruck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput!"
(Monty Python "Joke Warefare")
  Mit Zitat antworten Zitat
Andy090778

Registriert seit: 14. Mai 2006
23 Beiträge
 
Delphi 5 Standard
 
#6

Re: Thread sauber Beenden ? Handle ungültig.

  Alt 29. Jan 2008, 23:48
Ich hatte auch schon mal Probleme beim Ausführen von Code nach dem Beenden durch ein Terminate bzw. ein Free.

Mein Fazit:
  • - Ohne WaitFor darf man kein Thread manuell beenden. Klingt logisch, vor allem dann wenn der Thread Daten mit irgendwas anderem syncronisiert. Somit ist sichergestellt, dass alle Syncronized-Beziehungen beendet wurden.
  • - Aufräumarbeiten hab ich strikt in OnTerminated-Ereignis benutzt. Da ich sowieso außerhalb des Threads die Strukturen und Daten bereitstelle, hatte ich da kein Problem. Wenn Du eine Komponente machen möchtest, kannst Du ja trotzdem den Code in eine andere Unit packen.

Hier nochmal die Beschreibung aud Delphi:
Mit WaitFor ermitteln Sie den Wert von ReturnValue, wenn die Ausführung des Thread beendet ist. WaitFor kehrt erst zurück, wenn der Thread beendet ist. Dazu muß der Thread entweder die Methode Execute abschließen oder die Beendigung einleiten, sobald die Eigenschaft Terminated den Wert True annimmt.

Das fette ist der springende Punkt. Ich vermute mal bis die 1000ms Wartezeit vergehen, ist das Terminated Bit schon gesetzt und die internen Daten sind freigegeben. Dann ist auch Dein Handle nicht mehr gültig.

Abhilfe:

Ich würde ganz einfach mal:
Delphi-Quellcode:
    PdhCloseQuery(HQ);
    FreeLibrary(hPDH);
in eine Prozedur verschieben, die Du in der Prozedur Create an OnTerminate hängst.

Und aus
Array[1..4] of TPDHInfo würd ich auch ein Typ machen.

So lass ich zb. in einem Rechnungsprogramm verzögert die Daten aus einer Datenbank lesen und in ein Listview schreiben. Und der Benutzer kann jederzeit durch ESC o.ä. den Vorgang abbrechen.

Andreas
Andreas
  Mit Zitat antworten Zitat
Benutzerbild von Dani
Dani

Registriert seit: 19. Jan 2003
732 Beiträge
 
Turbo Delphi für Win32
 
#7

Re: Thread sauber Beenden ? Handle ungültig.

  Alt 30. Jan 2008, 01:13
Zitat von Andy090778:
...die Methode Execute abschließen oder die Beendigung einleiten, sobald die Eigenschaft Terminated den Wert True annimmt.
Das fette ist der springende Punkt. Ich vermute mal bis die 1000ms Wartezeit vergehen, ist das Terminated Bit schon gesetzt und die internen Daten sind freigegeben. Dann ist auch Dein Handle nicht mehr gültig.
Sleep kehrt erst zurück, wenn die Zeit abgelaufen ist. http://img145.imageshack.us/img145/4...teng101qt5.gif
Dani H.
At Least I Can Say I Tried
  Mit Zitat antworten Zitat
Andy090778

Registriert seit: 14. Mai 2006
23 Beiträge
 
Delphi 5 Standard
 
#8

Re: Thread sauber Beenden ? Handle ungültig.

  Alt 30. Jan 2008, 21:31
Hier verdeutlicht der Ablauf:

Delphi-Quellcode:
  PDHThread.Terminate;
// setzt Terminate im Thread, nun läuft die 1000ms Wartezeit ab
// ...
  PDHThread.Waitfor;
// Delphi wartet bis Terminated true wird oder Execute-Procedur beendet wird.
// ...
// Mittlerweile wird Terminated true
// Nun Springt Delphi zum nächsten Befehl
// Die Wartezeit ist noch nicht um
  PDHThread.Free;
// jetzt wird alles freigegeben
// Die Wartezeit ist noch nicht um

// Der Thread ruft jetzt erst die zwei anderen Befehle auf. Und hier knallts.
Andreas
  Mit Zitat antworten Zitat
Benutzerbild von Dani
Dani

Registriert seit: 19. Jan 2003
732 Beiträge
 
Turbo Delphi für Win32
 
#9

Re: Thread sauber Beenden ? Handle ungültig.

  Alt 30. Jan 2008, 22:00
Achso, da hab ich dich falsch verstanden . Allerdings stimmt das hier zumindest für Delphi 6 nicht, denn Terminated = true führt nicht dazu, dass Waitfor sofort zurückkehrt.
Delphi-Quellcode:
  PDHThread.Waitfor;
// Delphi wartet bis Terminated true wird oder Execute-Procedur beendet wird.
Dani H.
At Least I Can Say I Tried
  Mit Zitat antworten Zitat
Ralf107

Registriert seit: 17. Mär 2008
Ort: Berlin
4 Beiträge
 
Delphi 7 Professional
 
#10

Re: Thread sauber Beenden ? Handle ungültig.

  Alt 17. Mär 2008, 15:41
Ich schlage mich auch schon seit einiger Zeit mit der PDH.dll herum, bisher mit mäßigem Erfolg.

Beim Aufruf von PdhGetFormattedCounterValue() stürzt die Software ab, als ob stdcall nicht stimmt oder ein Varaiblentyp falsch deklariert ist.

Zitat von El.Blindo:
Delphi-Quellcode:
{...}
  _PDH_FMT_COUNTERVALUE = record
    CStatus : DWORD;
    longValue : Longint;
    doubleValue : double;
    largeValue : LONGLONG;
    AnsiStringValue : LPCSTR;
    WideStringValue : LPCWSTR;
  end;
{...}
Interessante Deklaration!

Zitat von msdn:
Delphi-Quellcode:
typedef struct _PDH_FMT_COUNTERVALUE
  DWORD CStatus; 
  union { // !!!
    LONG longValue;   
    double doubleValue;   
    LONGLONG largeValue;   
    LPCSTR AnsiStringValue;   
    LPCWSTR WideStringValue;
  }
;
} PDH_FMT_COUNTERVALUE, *PPDH_FMT_COUNTERVALUE;
Müsste der union nicht als varianter Record deklariert werden?

Delphi-Quellcode:
  _PDH_FMT_COUNTERVALUE = record
    CStatus : DWORD;
    case Integer of // !!!
     0 : ( longValue : Longint );
     1 : ( doubleValue : double );
     2 : ( largeValue : LONGLONG );
     3 : ( AnsiStringValue : LPCSTR );
     4 : ( WideStringValue : LPCWSTR );
  end;
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:57 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