So, nach mehrere Tagen glaube ich zu behaupten, mein Problem/Fehler eingegrenzt zu haben. Und ich vermute die Fehlerursache bei TOracleQuery bzw. dessen Anwendung.
Was macht das Programm
Das Programm habe ich soweit reduziert, wie es ging.
Jetzt ist nur noch ein Timer enthalten, der recht oft ein TOraclequery mit einer einfachen Select-Abfrage erstellt und ausführt. Das ganze im Threaded Mode (zugehörige OracleSession ist auch threadsafe eingestellt)
Im Original-Code ist der Timer eigentlich ein OracleEvent (habe ich nur zum Test durch einen Timer ersetzt)
wichtigstes Fehlersymptom
Programm ist plötzlich nicht mehr bedienbar (kann im ersten
Query passieren, oder erst nach 20 Minuten (bei durchschnittlich 1
Query / 5sek). Es läuift aber noch und im TaskManager wird konstant "wird ausgeführt" angezeigt. Besagtes Oracleevent löst auch nach wie vor (asynchron) aus. Kann man einen Breakpoint reinsetzen. An allen anderen Stellen im Code hilft ein Breakpoint nicht.
Es sieht also aus, wie ein Deadlock (ich vermute mal mit MsgWaitForMultipleObjects; müsste man aber nochmal nachbauen)
wann tritt das Problem auf
Der Deadlock tritt anscheinend auf, wenn ich das
Query-Objekt freigebe. Ein Problem tritt genauso auf (allerdings als
Exception), wenn ich das
Query-Objekt erneut nutze ohne es vorher freizugeben (ist dann als Feld der Klasse deklariert). Ich warte allerdings immer QueryThreadFinished ab und sende von da sogar noch eine Message via PostMessage und erst hier wird die Freigabe gestartet, oder das Objekt zur erneuten Nutzung freigegeben.
wichtiger Code
Delphi-Quellcode:
procedure TMainForm.LoadIPList;
//wird regelmäßig durch ein Event oder Timer ausgelöst
var Query:TOracleQuery;
begin
if FQueryCount=0
then
begin
Log('
LoadIPList');
FTempIPList:=TIPList.Create(true);
Query:=TORacleQuery.Create(self);
Query.Session:=ORacleSession;
Query.Threaded:=true;
Query.ThreadSynchronized:=true;
Query.OnThreadFinished:=QueryThreadFinished;
Query.OnThreadRecord:=QueryThreadRecord;
Query.OnThreadError:=QueryThreadError;
Query.SQLW:='
Select distinct SrcIP as x from dbt_Traffic';
Query.Execute;
inc(FQueryCount);
end;
end;
procedure TMainForm.QueryThreadFinished(
Query: TOracleQuery);
begin
log('
QueryThreadFinished');
postmessage(
handle,CM_QueryFree,0,integer(
Query));
end;
procedure TMainForm.CMQueryFree(
var msg: TMessage);
//message CM_QueryFree;
var del:boolean;
c:integer;
begin
if FQueryCount>0
then dec(FQueryCount);
//hier habe ich mit einer Schleife und sleep auch schon auf ThreadisRunning und Status=qsIdle gewartet, bringt auch nichts
TObject(msg.lparam).free;
Log('
QueryFree $'+inttohex(msg.LParam,8)+'
'+inttostr(FQueryCount));
end;
//QueryThreadRecord kann man komplett auskommentieren, der Fehler erscheint trotzdem.
//das Query kann ich auch als Feld der Klasse deklariern. Da müsste ich es nicht freigeben, allerdings kommt dann ein fehler bei LoadIPList (wahrscheinlich, wenn es sonst einen Deadlock gegeben hätte)
Was habe ich jetzt gemacht
Ich habe eine TObjectList, in der ich mir die
Query-Objekte merke und lösche in LoadIPList dann immer das vorletzte. Bisher ohne Probleme. Aber das kann ja nicht die Lösung sein. Gibt es da nicht etwas besseres? Ich habe damals leider nicht die Sourcen mitgekauft um da mal nachzusehen.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.