Pregunta

He creado una clase que abre un puerto COM y mangos superpuesto lectura y escritura. Contiene dos hilos independientes - uno que lee y que escribe datos. Ambos se llaman procedimientos OnXxx (por ejemplo OnRead o OnWrite) notificar acerca de la operación de lectura o escritura terminado.

El siguiente es un breve ejemplo de la idea de cómo funcionan los hilos:

  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;

Cuando nos fijamos en el procedimiento Close (), verá que entra en la sección crítica, termina el hilo por escrito y luego espera a que termine. Debido al hecho de que el hilo de la escritura puede poner en cola un nuevo valor a escribir cuando se llama al método OnWrite, que tratará de introducir la misma sección crítica cuando se llama al procedimiento Write () de la clase TAsyncSerialPort.

Y aquí tenemos un punto muerto. El hilo que llama al método Close () entró en la sección crítica y espera a que el hilo de la escritura a ser cerrada, mientras que al mismo tiempo que hilo espera para la sección crítica para ser liberados.

He estado pensando desde hace mucho tiempo y no me las arreglo para encontrar una solución a ese problema. La cosa es que me gustaría estar seguro de que ningún hilo de lectura / escritura está vivo cuando se deja el método Close (), lo que significa que no puedo acaba de establecer el indicador Terminado de esos temas y dejar.

¿Cómo puedo solucionar el problema? Tal vez debería cambiar mi enfoque para el manejo de puerto serie asíncrona?

Gracias por su consejo de antemano.

Mariusz.

--------- ---------- EDITAR
¿Qué tal una solución de este tipo?

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;

Si mi pensamiento es correcto, esto debería eliminar el problema de estancamiento. Desafortunadamente, sin embargo, cierro el puerto de comunicaciones manejar antes de que el hilo de la escritura está cerrado. Esto significa que cuando se llama a cualquier método que toma el mango puerto de comunicaciones como uno de sus argumentos (por ejemplo, escribir, leer, WaitCommEvent) una excepción debe ser planteado en ese hilo. ¿Puedo estar seguro de que si cojo esta excepción en ese hilo que no afectará el trabajo de toda la aplicación? Esta pregunta puede sonar estúpido, pero creo que algunas excepciones pueden hacer que el sistema operativo para cerrar la aplicación que lo causó, ¿verdad? ¿Hay que preocuparse de que en este caso?

¿Fue útil?

Solución

Sí, probablemente debería reconsiderar su enfoque. operaciones asincrónicas están disponibles exactamente a eliminar la necesidad de hilos. Si utiliza hilos, a continuación, utilizar las llamadas síncronas (bloqueo). Si utiliza las operaciones asincrónicas, y luego manejar todo en un único hilo -. No necesariamente el hilo principal, pero no tiene sentido de la OMI para hacer el envío y la recepción en diferentes hilos

Hay por supuesto maneras alrededor de su problema de sincronización, pero prefiero cambiar el diseño.

Otros consejos

Puede tomar la cerradura de la Cerca. En el momento en que vuelve de la WaitFor, el cuerpo de mensaje ha notado que se ha terminado, terminado el último bucle, y terminó.

Si usted no se siente feliz haciendo esto, entonces se podría mover ajustar el bloqueo justo antes de la FreeAndNil. Esto permite explícitamente los mecanismos de cierre de rosca funcionan antes de aplicar el bloqueo (por lo que no tendrá que competir con cualquier cosa por el bloqueo)

EDIT:

(1) Si también desea cerrar las comunicaciones manejan hacerlo después del bucle en el Ejecutar, o en el destructor de la rosca.

(2) Lo sentimos, pero su solución editado es un lío terrible. Esperar a terminar y va a hacer todo lo necesario, perfectamente segura.

El problema principal parece ser que coloque el contenido completo de Close en una sección crítica. Estoy casi seguro (pero tendrá que comprobar la documentación) que TThread.Terminate y TThread.WaitFor son seguros para llamar desde fuera de la sección. Tirando de la parte exterior de la sección crítica que va a resolver el punto muerto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top