我创建打开的COM端口一类和把手重叠读取和写入操作。它包含两个独立的线程 - 一个读取和一个写入数据。他们都称OnXXX程序(如OnRead或OnWrite)通知有关完成读或写操作。

以下是线程是如何工作的想法的一个短示例:

  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;

当你看关闭()过程,你将看到它进入临界区,终止写入线程,然后等待它完成。 因为事实是,当它调用OnWrite方法写入线程可以排队一个新的值写入的,它会尝试调用TAsyncSerialPort类的write()过程时输入相同的关键部分。

在这里,我们已经有了一个僵局。调用该Close()方法线程进入临界区,然后等待要关闭的写入线程,而在同一时间被释放的临界区线程等待。

我一直在想了很长一段时间,我没能找到解决这个问题。问题是,我想,以确保没有读/写线程是活着留在Close()方法时,这意味着我不能只设置这些线程终止标志和离开。

我该如何解决这个问题呢?也许我应该我的方法更改为异步处理串行端口?

感谢提前你的建议。

马里乌什。

--------- ---------- EDIT结果 如何这样的解决方案?

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;

如果我的想法是正确的,这应该消除死锁问题。然而不幸的是,我关闭通讯端口处理写入线程关闭之前。这意味着,当它调用采取通讯端口句柄作为它的一个参数的任何方法(例如写,读,WaitCommEvent)异常应在该线程得到提升。我可以肯定地说,如果我捕获了异常中的线程也不会影响整个应用程序的工作?这个问题听起来很蠢,但我觉得有些异常可能会导致操作系统关闭导致它的应用程序,对不对?我必须在这种情况下担心?

有帮助吗?

解决方案

是的,你应该重新考虑你的方法。异步操作是完全可以消除对螺纹的需求。如果你使用线程,然后使用同步(阻塞)调用。如果您使用异步操作,然后处理一切在一个线程 - 不一定是主线程,但它没有意义IMO做发送和在不同的线程接收

有大约您的同步问题当然办法,但我宁愿改变设计。

其他提示

您可以锁定了关闭的。通过它从WaitFor的返回时间,线主体已经注意到了这一点已被终止,完成了最后的循环,并结束了。

如果你感觉不到幸福这样做,那么你可以设置移动刚刚FreeAndNil之前锁定。这明确地让您将锁之前线程关闭机制的工作,(所以不会有什么关系的锁竞争)

编辑:

(1)如果你也想关闭通讯科处理在循环后做的执行,或者在线程的析构函数。

(2)抱歉,编辑后的溶液是一种可怕的混乱。终止与WAITFOR会做你需要的,完全安全的一切。

主要的问题似乎是,您将关闭整个内容中的关键部分。我几乎可以肯定(但你必须检查文档)是TThread.Terminate和TThread.WaitFor是安全的,从外面节调用。通过拉动临界部之外的部分,将解决僵局。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top