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() 메서드가 남아있을 때 읽기/쓰기 쓰레드가 생존하지 않는다는 것입니다. 즉, 그 쓰레드의 Terminated 플래그를 설정하고 나갈 수는 없습니다.
어떻게 문제를 해결할 수 있습니까? 어쩌면 직렬 포트를 비동기 적으로 처리하는 방식을 변경해야할까요?
미리 조언 해 주셔서 감사합니다.
마리우스.
--------- 편집 ----------
그런 해결책은 어떻습니까?
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;
제 생각이 맞으면 교착 상태 문제가 해결됩니다. 불행히도, 그러나 쓰기 포트가 닫히기 전에 통신 포트 핸들을 닫습니다. 즉, comm 포트 핸들을 인수 (예 : Write, Read, WaitCommEvent)로 사용하는 모든 메소드를 호출하면 해당 스레드에서 예외가 발생해야합니다. 해당 스레드에서 해당 예외를 잡으면 전체 응용 프로그램의 작업에 영향을 미치지 않는지 확인할 수 있습니까? 이 질문은 어리석은 소리 일지 모르지만 일부 예외로 인해 OS가 응용 프로그램을 종료하게 할 수 있다고 생각합니다. 맞습니까? 이 경우 걱정해야합니까?
나는 당신이 절대적으로 옳다고 생각한다. 그러나 왜이 방법을 선택했는지에 대한 이유가 있습니다. 동기 방식을 선택하면 마스크에 의해 지정된 이벤트가 발생할 때까지 WaitCommEvent() 메서드가 스레드를 차단합니다. 즉 이벤트가 발생하지 않으면 스레드를 닫을 수 없다는 의미입니다. 아니면 직렬 포트 핸들을 닫는 것만으로도 충분할 수 있으며 함수는 예외를 제외하고 리턴 할 것입니다. –
반면에, 나는 장치의 응답을 기다릴 수있는 두 개의 독립적 인 스레드를 생성한다. 모뎀과 통신한다고 가정 해 봅시다. 예를 들어 "AT"명령을 쓰면 모뎀이 "OK"또는 "ERROR"로 응답 할 것으로 예상됩니다. 이벤트 (TEvent)를 재설정하고, 이벤트가 읽기 스레드의 OnRead 메소드에 의해 설정 될 때 리턴하는 WaitForSingleObject() 메소드를 호출하십시오. 거기에 단 하나의 읽기/쓰기 스레드가 있다면, 나는 같은 일을 메인 응용 프로그램의 스레드를 차단해야 할 것이다. (. 맞아요? –
통신 할 단 하나의 직렬 포트가 있다면 아마도 비동기 I/O를 사용할 것입니다 하나 이상의 작업 스레드에서 비동기 I/O를 사용할 것입니다. 또한 백그라운드 스레드에서 동기식 또는 비동기식 I/O를 사용할지 여부는 데이터를 수신 할 때 미리 알고 있는지 여부에 달려 있습니다 WaitCommEvent()를 사용하면 왜 항상 WaitCommEvent()를 사용할 수 있습니까? 현명한 타임 아웃 값을 설정 한 후 직접 읽으십시오. 언제든지 상대방이 WaitCommEvent()를 보내면 더 많은 의미를 갖을 수 있습니다 .. – mghie