Frage

habe ich eine Klasse, die ein COM-Port und Griffe überlappten Lese- und Schreiboperationen öffnet. Es enthält zwei unabhängige Threads - eine, die liest und eine, die Daten schreibt. Beide nennen OnXXX Verfahren (zB OnRead oder OnWrite) Benachrichtigung über beendet Lese- oder Schreiboperation.

Hier finden Sie ein kurzes Beispiel der Idee, wie die Fäden arbeiten:

  TOnWrite = procedure (Text: string);

  TWritingThread = class(TThread)
  strict private
    FOnWrite: TOnWrite;
    FWriteQueue: array of string;
    FSerialPort: TAsyncSerialPort;
  protected
    procedure Execute; override;
  public
    procedure Enqueue(Text: string);
    {...}
  end;

  TAsyncSerialPort = class
  private
    FCommPort: THandle;
    FWritingThread: TWritingThread;
    FLock: TCriticalSection;
    {...}
  public
    procedure Open();
    procedure Write(Text: string);
    procedure Close();
    {...}
  end;

var
  AsyncSerialPort: TAsyncSerialPort;

implementation

{$R *.dfm}

procedure OnWrite(Text: string);
begin
  {...}
  if {...} then
    AsyncSerialPort.Write('something');
  {...}
end;

{ TAsyncSerialPort }

procedure TAsyncSerialPort.Close;
begin
  FLock.Enter;
  try
    FWritingThread.Terminate;
    if FWritingThread.Suspended then
      FWritingThread.Resume;
    FWritingThread.WaitFor;
    FreeAndNil(FWritingThread);

    CloseHandle(FCommPort);
    FCommPort := 0;
  finally
    FLock.Leave;
  end;
end;

procedure TAsyncSerialPort.Open;
begin
  FLock.Enter;
  try
    {open comm port}
    {create writing thread}
  finally
    FLock.Leave;
  end;
end;

procedure TAsyncSerialPort.Write(Text: string);
begin
  FLock.Enter;
  try
    {add Text to the FWritingThread's queue}
    FWritingThread.Enqueue(Text);
  finally
    FLock.Leave;
  end;
end;

{ TWritingThread }

procedure TWritingThread.Execute;
begin
  while not Terminated do
  begin
    {GetMessage() - wait for a message informing about a new value in the queue}
    {pop a value from the queue}
    {write the value}
    {call OnWrite method}
  end;
end;

Wenn Sie an der Close () Verfahren betrachten, werden Sie sehen, dass es in den kritischen Abschnitt eintritt, beendet die Schreib Thread und wartet dann darauf, es zu beenden. Aufgrund der Tatsache, dass der Schreib Thread kann einen neuen Wert enqueue geschrieben werden, wenn es die OnWrite Methode aufruft, wird er versuchen, den gleichen kritischen Abschnitt einzugeben, wenn die Write () Prozedur der TAsyncSerialPort-Klasse aufrufen.

Und hier haben wir eine Sackgasse bekam. Der Faden, der das Schließen () Methode trat in den kritischen Abschnitt genannt, und wartet dann auf das Schreibgewinde geschlossen werden, während zur gleichen Zeit die Thread wartet auf den kritischen Abschnitt befreit werden.

Ich habe für eine recht lange Zeit zu denken und ich habe es nicht geschafft, eine Lösung für dieses Problem zu finden. Die Sache ist, dass ich sicher sein möchte, dass kein Lesen / Schreiben Thread am Leben ist, wenn die Close () Methode gelassen wird, was bedeutet, dass ich nicht nur die Abgebrochene Flagge dieser Threads festlegen kann und verlassen.

Wie kann ich das Problem lösen? Vielleicht sollte ich mein Ansatz ändern asynchron serielle Schnittstelle zu umgehen?

Vielen Dank für Ihre Beratung im Voraus.

Mariusz.

--------- ---------- EDIT
Wie wäre eine solche Lösung?

procedure TAsyncSerialPort.Close;
var
  lThread: TThread;
begin
  FLock.Enter;
  try
    lThread := FWritingThread;
    if Assigned(lThread) then
    begin
      lThread.Terminate;
      if lThread.Suspended then
        lThread.Resume;
      FWritingThread := nil;
    end;

    if FCommPort <> 0 then
    begin
      CloseHandle(FCommPort);
      FCommPort := 0;
    end;
  finally
    FLock.Leave;
  end;

  if Assigned(lThread) then
  begin
    lThread.WaitFor;
    lThread.Free;
  end;
end;

Wenn mein Denken richtig ist, sollte dies das Deadlock-Problem beseitigen. Leider aber schließe ich die COM-Port handhaben, bevor der Schreib Thread geschlossen ist. Das bedeutet, dass, wenn es ruft jede Methode, die den Comm Port Griff als eine seiner Argumente annimmt (zB Schreiben, Lesen, WaitCommEvent) eine Ausnahme in diesem Thread angehoben werden soll. Kann ich sicher sein, dass, wenn ich diese Ausnahme in diesem Thread fangen wird es nicht die Arbeit der gesamten Anwendung auswirken? Diese Frage klingt vielleicht dumm, aber ich denke, dass einige Ausnahmen das Betriebssystem verursachen können die Anwendung zu schließen, die sie verursacht hat, nicht wahr? Muss ich etwa, dass in diesem Fall Sorgen machen?

War es hilfreich?

Lösung

Ja, sollten Sie vielleicht Ihr Konzept überdenken. Asynchrone Operationen zur Verfügung stehen genau den Bedarf an Fäden zu beseitigen. Wenn Sie Threads verwenden, dann synchron (Blockierung) Anrufe verwenden. Wenn Sie asynchrone Operationen verwenden, dann ist alles in einem Thread behandeln -. Nicht unbedingt der Haupt-Thread, aber es macht keinen Sinn IMO das Senden und Empfangen in verschiedenen Threads zu tun

Es gibt natürlich Möglichkeiten, um Ihr Synchronisierungsproblem, aber ich würde eher das Design ändern.

Andere Tipps

können Sie nehmen die Verriegelung aus dem Schließen. Zu der Zeit, sie aus dem WaitFor zurückkehren, ist der Faden Körper bemerkt es beendet wurde, abgeschlossen die letzte Schleife und beendet.

Wenn Sie nicht glücklich fühlen, dies zu tun, dann könnte man Setzen der Sperre bewegen kurz vor dem FreeAndNil. Auf diese Weise kann explizit die Fäden Shutdown Mechanismen funktionieren, bevor Sie die Sperre gelten

(so wird es nicht mit allem, was für die Sperre hat zu konkurrieren)

EDIT:

(1) Wenn Sie auch behandeln die Comms schließen es nach der Schleife tun in der Execute oder in dem destructor Thread.

(2) Es tut uns leid, aber Ihre bearbeiteten Lösung ist eine schreckliche Chaos. Terminate und waitfor werden alles tun, was Sie brauchen, vollkommen sicher.

Das Hauptproblem scheint zu sein, dass Sie den gesamten Inhalt der Nähe in einem kritischen Abschnitt platzieren. Ich bin fast sicher (aber Sie werden die Dokumentation überprüfen müssen), dass TThread.Terminate und TThread.WaitFor sicher sind von außerhalb des Abschnitts zu nennen. dass ein Teil außerhalb des kritischen Abschnitt durch Ziehen werden Sie aus der Sackgasse lösen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top