質問

私は、COMポートをオープンしたクラスを作成し、ハンドルは読み取りおよび書き込み操作を重複していました。読み込み1とのデータの書き込みを行う1 - これは、2つの独立したスレッドが含まれています。それらの両方が終了した読み取りまたは書き込み操作に関する通知(例えばOnReadまたはOnWrite)XXXの手続きを呼び出します。

以下は、スレッドがどのように動作するかのアイデアの短い例があります:

  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クラスの書き込み()プロシージャを呼び出すときに同じクリティカルセクションを入力しようと書き込まれる新しい値をキューに入れることができるという事実のため。

そして、ここでは、デッドロックを持っています。同時に、そのスレッドが解放されるクリティカルセクションを待機しながら、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;

私の考えが正しければ、これはデッドロックの問題を解消する必要があります。書き込みスレッドが閉じられる前に、しかし、残念ながら、私は、通信ポートのハンドルを閉じます。これは、引数の1つ(例えば、読み取り、WaitCommEventを書く)と通信ポートのハンドルを取る任意のメソッドを呼び出すときに例外がそのスレッドに提起しなければならないことを意味しています。私はそのスレッドでその例外をキャッチした場合、それはアプリケーション全体の作業に影響を与えないことを確認することができますか?この質問は愚かに聞こえるかもしれないが、私はいくつかの例外は、右、それを引き起こしたアプリケーションを閉じるようにOSを引き起こすかもしれないと思いますか?私はこのときのことを心配する必要がありますか?

役に立ちましたか?

解決

はい、あなたはおそらく、あなたのアプローチを再考する必要があります。非同期操作は、スレッドの必要性を排除するために、正確にご利用いただけます。あなたがスレッドを使用する場合は、同期(ブロッキング)呼び出しを使用します。あなたは非同期操作を使用する場合は、1つのスレッドですべてを処理 - 必ずしもメインスレッドが、それはIMO送信と異なるスレッドでの受信を行うには意味がありません。

があり、あなたの同期の問題の周りのコースのいくつかの方法ですが、私はむしろデザインを変更したい。

他のヒント

あなたは閉じるのロックアウトを取ることができます。それはWAITFORから戻った時点で、スレッド本体は、それが終了しているに気づいた最後のループを完了し、終了しました。

あなたがこれをやって幸せに感じていない場合は、

、そしてあなただけFreeAndNil前にロックを設定し移動することができます。これは、明示的にロックを適用する前に、スレッドのシャットダウンメカニズムが働くことができます(それはロックのために何と競合する必要はありませんので)。

EDITます:

(1)あなたも途切れが実行中のループの後、またはスレッドのデストラクタでそれを行う取り扱うクローズします。

(2)申し訳ありませんが、編集したソリューションはひどい混乱です。終了し、WAITFORは完全に安全に、あなたが必要なすべてを行います。

主な問題は、クリティカルセクションで閉じるの全体の内容を置くことのようです。私はほとんど確信している(ただし、ドキュメントをチェックする必要があります)TThread.TerminateとTThread.WaitForはセクション外部から呼び出すことが安全であること。クリティカルセクション外でその部分を引っ張って、あなたはデッドロックを解決します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top