Einzelnen Beitrag anzeigen

Medium

Registriert seit: 23. Jan 2008
3.686 Beiträge
 
Delphi 2007 Enterprise
 
#24

AW: For-Schleife im Thread wird nur einmal abgearbeitet

  Alt 15. Jun 2014, 18:03
Was für Listen genau hängt natürlich davon ab, was du für Daten hin und her schieben willst. Ich habe meist eine Hand voll kleiner Daten-Klassen, die die nötigen Infos aufnehmen, und werfe diese dann in meine Listen. Wenn es nur um Strings geht, ist eine StringList natürlich angenehm fertig.

Um den zweiten Teil zu erklären mal zwei Code-Fetzen:

Zum einen die "faule" Variante:
Delphi-Quellcode:
{uDataLists.pas}
TMyDataContainer = class
public
  x, y, z: Integer;
end;


{uMyThread.pas}
TMyThread = class(TThread)
private
  MainForm: THandle; // Im Konstruktor übergeben, dann kann das von PostMessage genutzt werden, und der Thread könnte jedes beliebige Form bedienen.
  ...
public
  MainFormDataList: TList<TMyDataContainer>;
  ...
end;

{uMainForm.pas}
TMainForm = class(TForm)
private
  ThreadDataList: TList<TMyDataContainer>;
  MyThread: TMyThread;
...
public
...
end;
Und etwas netter:
Delphi-Quellcode:
{uDataLists.pas}
TMyDataContainer = class
public
  x, y, z: Integer;
end;

{uThreadCommunications.pas}
TMyThreadToFormCommunicator = class
private
  class var FFromFormToThread: List<TMyDataContainer>;
  class var FFromThreadToForm: List<TMyDataContainer>;
  class var FCriticalSection: TCriticalSection;
public
  class procedure PutInFormToThread(aDataContainer: TDataContainer);
  class function GetFromFormToThread: TDataContainer;
  ...
end;

class procedure TMyThreadToFormCommunicator.PutInFormToThread(aDataContainer: TDataContainer);
begin
  FCriticalSection.Enter;
  try
    FFromFormToThread.Add(aDataContainer);
  finally
    FCriticalSection.Leave;
  end;
end;

class function TMyThreadToFormCommunicator.GetFromFormToThread: TDataContainer;
begin
  FCriticalSection.Enter;
  try
    if FFromFormToThread.Count > 0 then
    begin
      result := FFromFormToThread.Items[0];
      FFromFormToThread.Remove(result);
    end
    else
      result := nil;
  finally
    FCriticalSection.Leave;
  end;
end;


{uMyThread.pas}
TMyThread = class(TThread)
private
  MainForm: THandle; // Im Konstruktor übergeben, dann kann das von PostMessage genutzt werden, und der Thread könnte jedes beliebige Form bedienen.
  ...
public
  ...
end;

procedure TMyThread.Execute;
begin
  repeat
    try
      if FormNeedsUpdate then
      begin
        TMyThreadToFormCommunicator.PutInThreadToForm(currentDataContainer);
        PostMessage(FMainForm, WM_THREADUPDATE, 0, 0);
      end;
      if TMyThreadToFormCommunicator.FromFromToThreadCount > 0 then
      begin // Ob man hier immer nur ein Element verarbeitet oder sofort alle anstehenden ist Ermessenssache und vom genauen Zweck anhängig
        dataContainer := TMyThreadToFormCommunicator.GetFromFromToThread;
        DoStuffWith(dataContainer);
        dataContainer.Free;
      end;
      Sleep(1); // CPU Zeit abgeben um 100%-Auslastung in ruhigen Phasen zu vermeiden
    except
      // Allgemeine Exceptionbehandlung
    end;
  until Terminated;
end;
// Thread nutzt dann TMyThreadToFormCommunicator.PutInThreadToForm() und TMyThreadToFormCommunicator.GetFromFormToThread


{uMainForm.pas}
TMainForm = class(TForm)
private
  MyThread: TMyThread;
  procedure ThreadUpdateHandler(var Msg: TMessage); message WM_THREADUPDATE;
...
public
...
end;

procedure TMainForm.ThreadUpdateHandler(var Msg: TMessage);
var
  dataContainer: TMyDataContainer;
begin
  dataContainer := TMyThreadToFormCommunicator.GetFromThreadToForm;
  DoStuffWith(dataContainer);
  dataContainer.Free;
end;
// Form nutzt dann TMyThreadToFormCommunicator.PutInFormToThread() und TMyThreadToFormCommunicator.GetFromThreadToForm
Die Datencontainerklassen müssen natürlich nicht identisch sein, ich wollte es nur nicht noch länger machen. Bei der faulen Variante halst man sich ggf. im Nachgang mehr Arbeit auf, da die gesamte Synchronisation noch passieren muss. Alternativ erstellt man sich eine generische Listenklasse, die von sich aus alle Zugriffe synchronisiert. Das sieht nach einem Haufen Vorbau aus, erspart einem später aber Ärger mit der Sync und ist im Handling dann später wirklich geschmeidig. Dieser Weg erhebt aber keinen Anspruch auf Allgemeingültigkeit, es gibt sicherlich Szenarien, in denen das völlig verkehrt sein kann. Bei mir passte eine solche oder ähnliche Struktur halt sehr oft ins Konzept.
"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