[Delphi] Thread nog niet klaar als object al weg is

Pagina: 1
Acties:
  • 141 views sinds 30-01-2008
  • Reageer

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Ik heb een afgeleide van TIdTCPClient (Indy) gemaakt welke een thread aanmaakt die controleert of er nieuwe data beschikbaar is. De thread wordt pas aangemaakt als er verbinding is gemaakt en wordt weer vrijgegeven als de verbinding wordt verbroken.

De disconnect van de TTCPClient ziet er als volgt uit:
code:
1
2
3
4
5
procedure TTCPClient.Disconnect;
begin
  if Assigned(FTCPClientReader) then FTCPClientReader.Terminate;
  inherited Disconnect;
end;


Nou is het in sommige gevallen (dus lang niet altijd) zo dat als je disconnect aanroept en gelijk free dan is de thread nog niet helemaal klaar. Deze geeft dus een AV (op adres 000000...). Dit klopt wel omdat het TCPClient object waar de thread naar kijkt al is vrijgegeven.

De execute van de thread zit er als volt uit:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
procedure TTCPClientReader.Execute;
var
  InpSize: Integer;
begin
  while (not Terminated) and (FTCPClient.Connected) do
  begin
    try
      InpSize := FTCPClient.InputBuffer.Size;
      if (InpSize = 0) or (InpSize = InpLastSize) then
        InpLastSize := InpSize + FTCPClient.ReadFromStack(False, -1, False);
      if FTCPClient.Connected then
        Synchronize(FTCPClient.DoDataAvailableEvent);
    except
      Terminate;
    end;
  end;
end;


Ik heb getest of mijn theorie klopt door een sleep(500) te zetten tussen TCPClient.Disconnect en TCPClient.Free, en dan gaat het inderdaad wel goed (een sleep(1) is al genoeg (!))

Heeft iemand een idee hoe ik dit goed krijg zodat hij wacht totdat de thread klaar is en dan pas destroy doet?

Heb het probleem is een zeer simpel project kunnen vatten: Delphi 7 source.

[ Voor 5% gewijzigd door jelmervos op 05-02-2003 22:14 ]

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 22-06 10:32

_Thanatos_

Ja, en kaal

FTCPClientReader.WaitFor gebruiken?

日本!🎌


  • ProgrammerX
  • Registratie: Juli 2002
  • Laatst online: 26-02-2021
Misschien kun je eens kijken naar de api "WaitForSingleObject". Als ik me niet vergis kun je hiermee namelijk threads laten wachten op een event (event stuur je door een api behorend bij de hierboven genoemnde api).

Dus ik denk dat je voor de destroy de api "WaitForSingleObject" moet aanroepen en als de thread klaar is, stuurt deze dan het event naar dat object zodat de destroy wordt uitgevoerd.

Ben alleen even aan het twijfelen of je "destroy" code dan ook niet in een thread moet staan, maar zoek maar eens in de delphi/win32 help op die api en google maar eens ff. Ik denk dat je het dan zo wel gevonden hebt :)

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 21:28

Creepy

Tactical Espionage Splatterer

Welke Delphi versie gebruik je? In delphi 4 (en 5 dacht ik ook) is dit een known bug...

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
WaitFor geeft me een dead-lock... volgens mij is het niet zo slim om WaitFor aan te roepen in de VCL (main) thread als je synchronize gebruikt in je thread.

Ik gebruik Delphi 7 Enterprise.

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Uuhmm, dit is genoeg om het goed te laten werken maar is toch wel vreemd:
code:
1
2
3
4
5
6
7
8
9
procedure TTCPClient.Disconnect;
begin
  if Assigned(FTCPClientReader) then
  begin
    FTCPClientReader.Terminate;
    WaitForSingleObject(FTCPClientReader.Handle, 0);
  end;
  inherited Disconnect;
end;

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 13-08 16:46

curry684

left part of the evil twins

Delphi schreef op 05 February 2003 @ 12:48:
WaitFor geeft me een dead-lock... volgens mij is het niet zo slim om WaitFor aan te roepen in de VCL (main) thread als je synchronize gebruikt in je thread.
Uhm nee :)

En die laatste oplossing die je geeft is ook een hit&miss want die doet hetzelfde als de WaitFor method intern. Correct is om de synchronize niet uit te voeren indien de thread terminating is, maar de State variable is niet threadsafe te benaderen als je de Terminate method gebruikt dus dan heb je alsnog kans op problemen.

De enige correcte oplossing is om met CreateEvent een shutdown event te alloceren welke je bij het verlaten van de thread aanschopt. De mainthread kan dan met WaitForSingleObject wachten op dat event voordat ie delete.

Professionele website nodig?


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Ok, daar heb ik een voorbeeld van gezien via Google, dus zal even kijken of dat gaat lukken. Bedankt.

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Helaas werkt dat ook niet. De main thread blijft gewoon hangen, en dat komt denk ik wederom door de synchronize in de thread die weer wacht op de main thread (dead-lock dus).

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Ik kom er niet uit! Heb het probleem is een zeer simpel project kunnen vatten: Delphi 7 source.

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 13-08 16:46

curry684

left part of the evil twins

Okee nog een oplossing maar nu wordt ie omslachtig en ik vind moeilijk te geloven dat het zover moet, maja....

Open een 3e thread, een 'monitor thread' die enkel en alleen pollt of de TCP thread nog leeft, en houdt die in de gaten. Deze kun je dan ook gebruiken om de 2e thread opnieuw te jongen als er iets fout meegaat (2 vliegen in 1 klap).

Professionele website nodig?


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 21-08 17:14
curry684 schreef op 05 February 2003 @ 14:11:
De enige correcte oplossing is om met CreateEvent een shutdown event te alloceren welke je bij het verlaten van de thread aanschopt. De mainthread kan dan met WaitForSingleObject wachten op dat event voordat ie delete.
Nee hoor, ik heb een andere gevonden; Queued User APCs kun je hier uitstekend voor misbruiken; die kunnen namelijk een I/O operatie onderbreken( en ook een Wait...Ex of Sleep...Ex). Queue een APC die een exit_now flag zet, test na elke APC interruptie of de exit_now flag staat, zo ja, exit thread.
Evengoed staat de mainthread na de QueueUserAPC( ) functie dan te wachten met een WaitForSingleObjectEx.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Vreemd dat het op die manier moet, misschien doe ik toch ergens anders iets goed verkeerd. Iemand die eens naar de source wil kijken (start um maar eens in de debugger).

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 13-08 16:46

curry684

left part of the evil twins

MSalters schreef op 05 February 2003 @ 23:34:
Nee hoor, ik heb een andere gevonden; Queued User APCs kun je hier uitstekend voor misbruiken; die kunnen namelijk een I/O operatie onderbreken( en ook een Wait...Ex of Sleep...Ex). Queue een APC die een exit_now flag zet, test na elke APC interruptie of de exit_now flag staat, zo ja, exit thread.
Evengoed staat de mainthread na de QueueUserAPC( ) functie dan te wachten met een WaitForSingleObjectEx.
Deze oplossing heb ik express niet aangehaald omdat die Synchronize functie van TThread in principe niets anders is dan een UserAPC aanroepen op de main thread. Lijkt me dus dat het weinig op zal lossen.

Als je I/O operaties wil onderbreken is een overlapped operatie met event objects sowieso netter. Ik heb APC's wel gebruikt maar ze hebben het nooit overleefd omdat ze niet broodnodig waren en door charmantere constructies te vervangen waren.
Delphi schreef op 05 February 2003 @ 23:47:
Vreemd dat het op die manier moet, misschien doe ik toch ergens anders iets goed verkeerd. Iemand die eens naar de source wil kijken (start um maar eens in de debugger).
Binnenkort heb ik wel een Delphi-install, tot die tijd moet je het doen met m'n BCB-kennis en kan ik je sources niet compilen ;)

Professionele website nodig?


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
curry684 schreef op 06 February 2003 @ 01:09:
[...]

Binnenkort heb ik wel een Delphi-install, tot die tijd moet je het doen met m'n BCB-kennis en kan ik je sources niet compilen ;)
Ok, geen probleem. Misschien dat iemand anders snapt wat hier mis is.

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Iemand misschien het voorbeeld project bekeken en weet wat er verkeerd gaat?

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Wat er mis is? Dat was toch al duidelijk? Je Freeed de TTCPClient terwijl de thread nog draaid welke die instantie ook gebruikt. De oplossing is dat niet te doen, natuurlijk :)

Synchronize stuurt een windows message naar the main (GUI) thread en zorgt daardoor dat alles in de pas blijft lopen. WaitFor suspend de main thread en zorgt dus voor een deadlock in combinatie met Synchronize. Dit betekend dat alle oplossing ism Synchronize niet gaan werken als ze de main thread suspenden.

Probeer eens op een andere manier een flag te zetten. Wat dacht je van een extra variable in je thread die aangeeft of de Execute echt afgelopen is? In de Disconnect blijf je net zolang loopen totdat dat gebreurt is. Pas ook op je FreeOnTerminate. Deze kan er voor zorgen dat de Thread al gefreeed is terwijl je nog op de vlag aan het controleren bent. Of een OnTerminate even van die thread waar die TTCPClient actie op kan ondernemen, of aangezien ze toch al een relatie hebben kan de thread ook wel direct een functie van TTCPClient aanroepen.
Delphi:
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
procedure TTCPClient.Disconnect;
begin
  if Assigned(FTCPClientReader) then begin
    FTCPClientReader.Terminate;

    while not FTCPClientReader.Done do begin
      Sleep(10);
      Application.ProcessMessages;
    end;

    FTCPClientReader.Free;
  end;

  inherited Disconnect;
end;

constructor TTCPClientReader.Create(TCPClient: TTCPClient);
begin
  inherited Create(False);
  InpLastSize := 0;
  FTCPClient := TCPClient;
  FDone := False;
//  FreeOnTerminate := True;
end;

procedure TTCPClientReader.Execute;
var
  InpSize: Integer;
begin
  try
    while (not Terminated) and (FTCPClient.Connected) do
    begin
      ...
    end;
  finally
    FDone := True;
  end;
end;


Het is niet de mooiste oplossing misschien, maar het is wel een werkende. :) De kleine Sleep zit er in om geen 100% belasting te krijgen. Het duurt namelijk even voordat de thread echt stopt. Dat komt, zo te zien, door de ReadFromStack functie welke nogal wat seconden duurt voordat ie verder gaat. Hoe korter ie daar over doet hoe sneller de thread afsluit, maar dat is aan jouw om daar wat aan te doen of niet.

We adore chaos because we like to restore order - M.C. Escher


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Grappig, zat es naar je code te kijken en probeerde dit:
Delphi:
1
2
3
4
5
6
7
8
9
10
procedure TTCPClient.Disconnect;
begin
  if Assigned(FTCPClientReader) then
    FTCPClientReader.Terminate;

  inherited Disconnect;

  if Assigned(FTCPClientReader) then
    FreeAndNil(FTCPClientReader);
end;

Is dit niet genoeg dan? Het lijkt te werken. Natuurlijk heb ik de FreeOnTerminate op False gezet.

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Nee, dat zou niet uit moeten maken voor zover ik weet. Ook de Destory zet terminated up True.

Je kan je thead ook killen met TerminateThread als je er meteen vanaf wilt.

We adore chaos because we like to restore order - M.C. Escher


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Is TerminateThread een slechte oplossing?

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Het killed je thread en de destructor zal nooit aangeroepen worden. Er bestaat dus een kans dat bepaalde resources blijven bestaan. Dat is natuurlijk zonde, maar als dat geen probleem is ben je natuurlijk wel in 1 klap van al je thread problemen af :D

We adore chaos because we like to restore order - M.C. Escher


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Het werkt idd perfect met TerminateThread. Heb de help es nagelezen over deze method en er staat idd dat je moet uitkijken met deze method. Maar als je totale contole hebt over wat de thread doet dan moet het geen probleem zijn.
Nouja, gebruik hem eerst wel totdat ik iets beters heb. Bedankt!

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 13-08 16:46

curry684

left part of the evil twins

TerminateThread kan pijnlijk zijn als je proces niet als geheel afsluit naderhand, tis een resource leak tenslotte. Als het alleen bij het afsluiten van het programma gebeurd en je weet zeker dat je niets verneukt lijkt het me geen probleem.

Professionele website nodig?


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Mensen, in de thread execute stond dit:
Delphi:
1
2
      if (InpSize = 0) or (InpSize = InpLastSize) then
        InpLastSize := InpSize + FTCPClient.ReadFromStack(False, -1, False);

Deze code checkt of er nieuwe data is. Waar ReadFromStack er als volgt uitziet:
Delphi:
1
2
function TIdTCPConnection.ReadFromStack(const ARaiseExceptionIfDisconnected: Boolean = True;
 ATimeout: Integer = IdTimeoutDefault; const ARaiseExceptionOnTimeout: Boolean = True): Integer;

Dus wat is er fout? Idd, parameter 1 moest op True staan zodat hij altijd een exceptie geeft als de verbinding is verbroken. Mijn fout dit.

Maar bedankt voor het mee denken, was zeer interessant.

[ Voor 3% gewijzigd door jelmervos op 09-02-2003 12:31 ]

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 13-08 16:46

curry684

left part of the evil twins

Delphi schreef op 09 February 2003 @ 12:31:
Dus wat is er fout? Idd, parameter 1 moest op True staan zodat hij altijd een exceptie geeft als de verbinding is verbroken. Mijn fout dit.

Maar bedankt voor het mee denken, was zeer interessant.
Ja daar kunnen we je niet mee helpen als je iets verknalt in de exotische componenten, die ken ik ook niet allemaal :P Succes verder!

Professionele website nodig?


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
exotisch? Is Indy hoor, sinds Delphi 7 standaard in Delphi. :)

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 21:28

Creepy

Tactical Espionage Splatterer

Delphi schreef op 10 februari 2003 @ 23:56:
exotisch? Is Indy hoor, sinds Delphi 7 standaard in Delphi. :)
In delphi 6 toch al? ;)

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Topicstarter
Creepy schreef op 11 February 2003 @ 00:04:
[...]

In delphi 6 toch al? ;)
Oja, idd. Scherp! :) Nouja, iig niet exotisch te noemen lijkt mij.

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 13-08 16:46

curry684

left part of the evil twins

Zit niet in BCB6 voor zover ik weet, dus voor mij exotisch :P

* curry684 heeft vanmiddag gedwongen Delphi 5 moeten installeren voor z'n werk...

Professionele website nodig?


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Ik durf het niet 100% zeker te zeggen, maar aangezien BCB6 later uitkwam als D6 verwacht ik van wel eigenlijk.

We adore chaos because we like to restore order - M.C. Escher

Pagina: 1