Einzelnen Beitrag anzeigen

Medium

Registriert seit: 23. Jan 2008
3.679 Beiträge
 
Delphi 2007 Enterprise
 
#1

[MySQL, UniDAC] Query vorzeitig abbrechen?

  Alt 28. Feb 2016, 13:03
Datenbank: MySQL • Version: 5.6.16 • Zugriff über: UniDAC
Ahoi DP,

ich lasse mir in einem Thread Daten zur Anzeige in einem Graphen aus einer MySQL DB holen. Dies mache ich immer, wenn jemand im Graphen die Achsen verschiebt oder den X-Zoom ändert. Das klappt bisher auch recht gut. Leider passiert es aber insbesondere beim Zoom mit dem Scrollrad, dass eine neue Abfrage gestartet wird bevor die davor fertig ist. Durch das entsprechend nötige Syncen führt das am Ende dazu, dass man nur maximal so schnell scrollen kann, wie die DB die Daten holt.
Oder genauer: Man sieht das Endergebnis erst dann, wenn auch der letzte Thread fertig geworden ist - welcher leider noch nichtmals immer der ist, der zuletzt gestartet wurde, wodurch man manchmal unvollständige Graphen bekommt, da sie noch zu einem anderen Zeitabschnitt oder Zoom gehören.

Aktuell behelfe ich mir, in dem ich ggf. noch laufende Threads bei Eingang einer neuen Zoom- oder Verschiebeoperation versuche sich "gracefully" beenden zu lassen, und falls das nicht innerhalb von 100ms passiert ist, den Thread mit KillThread() brutal wegzuschießen. Das geht! Aber leider befürchte ich dadurch massive Speicherlöcher und irgendwann auch Handle-Knappheit, da sich natürlich jeder Thread eine eigene UniConnection und UniQuery erstellt, die beim Abschießen wahrscheinlich nicht freigegeben werden, und ich keine Ahnung habe, was auf Seiten des SQL Servers noch offen bleibt.

Gibt es irgend eine Möglichkeit eine gerade laufende Query vor Rückkehr abzubrechen? Das wäre die mir liebste und einfachste Lösung.


Delphi-Quellcode:
procedure TKATChartUpdateThread.Execute;
var
  con: TUniConnection;
  qry: TUniQuery;
  startDate, endDate: TDateTime;
  tmpSeries: TObjectList;
  seriesIDs: TList;
  aborted: Boolean;
begin
  aborted := false;
  seriesIDs := nil;
  Screen.Cursor := crAppStart;
  con := TUniConnection.Create(nil);
  con.Server := FChart.FConnection.Server;
  con.Database := FChart.FConnection.Database;
  con.Username := FChart.FConnection.Username;
  con.Password := FChart.FConnection.Password;
  con.ProviderName := FChart.FConnection.ProviderName;
  con.Connect;
  qry := TUniQuery.Create(nil);
  qry.Connection := con;

  if Abort then begin aborted := true; Exit; end;

  try
    startDate := UnixToDateTime(FChart.FLeftXValue);
    endDate := UnixToDateTime(FChart.FRightXValue);

    if Abort then begin aborted := true; Exit; end;

    tmpSeries := TObjectList.Create(true);

    FChart.FSeriesIDLock.Enter;
    try
      seriesIDs := TList.Create;
      seriesIDs.Assign(FChart.FSeriesIDs);
    finally
      FChart.FSeriesIDLock.Leave;
    end;

    if Abort then begin aborted := true; tmpSeries.Free; Exit; end;

    for i := 0 to seriesIDs.Count-1 do
    begin
      k := tmpSeries.Add(TKATChartSeries.Create(Integer(seriesIDs[i])));
      qry.SQL.Text := BuildSumSQL(Integer(seriesIDs[i]), con);
      qry.ParamByName('t1').AsDateTime := startDate;
      qry.ParamByName('t2').AsDateTime := endDate;
      qry.Open;
      while not qry.Eof do
      begin
        TKATChartSeries(tmpSeries[k]).AddValue(qry.FieldByName('qMIN').AsFloat,
                                               qry.FieldByName('qMAX').AsFloat,
                                               qry.FieldByName('qAVG').AsFloat,
                                               DateTimeToUnix(qry.FieldByName('qDate').AsDateTime));
        qry.Next;
        if Abort then begin aborted := true; tmpSeries.Free; Exit; end;
      end;
      qry.Close;
    end;

    if Abort then begin FChart.IsBusy := false; aborted := true; tmpSeries.Free; Exit; end;

    FChart.FSeriesLock.Enter;
    try
      if Assigned(FChart.FSeries) then
        FreeAndNil(FChart.FSeries);
      FChart.FSeries := tmpSeries;
    finally
      FChart.FSeriesLock.Leave;
    end;
  finally
    Screen.Cursor := crDefault;
    qry.Free;
    con.Free;
    if Assinged(seriesIDs) then
      seriesIDs.Free;
    if not aborted then
      Synchronize(FChart.UpdateReady);
  end;
end;
Die ganzen "if Aborted then ..." sind mein Versuch den Thread vernünftig zu beenden. Die Aufrufende Methode sieht so aus:

Delphi-Quellcode:
procedure TKATChart.MakeSeries;
begin
  if Assigned(FUpdateThread) then
  begin
    FUpdateThread.Abort := true;
    if WaitForSingleObject(FUpdateThread.Handle, 100) = WAIT_TIMEOUT then
      TerminateThread(FUpdateThread.Handle, 0);
  end;
  FUpdateThread := TKATChartUpdateThread.Create(self);
end;
Die einzige Operation die in dem Thread potenziell lange dauert ist die Zeile qry.Open; . Aber da komme ich auf keine mir bekannte Art mehr zwischen. Was kann man hier tun? Insbesondere fürchte ich, dass nach dem TerminateThread die DB noch immer denkt das Ergebnis wird gebraucht, und die Operation unnötig zu Ende führt, was mich Performance bei allen nachfolgenden Queries kosten dürfte. Und Ressourcen die dauerhaft offen bleiben. Sehr unschön.
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat