2009-08-23 4 views
7

모두 안녕하세요!C에서 SerialPort 및 스레드로 "안전한 핸들이 닫혔습니다."

이 스레드 포트는 직렬 포트에서 한 행으로 읽는 SerialPort 래퍼입니다. 여기에 내 스레드 코드가 있습니다. 내가 myThread.Abort();를 호출 할 때

protected void ReadData() 
{ 
    SerialPort serialPort = null; 
    try 
    { 
     serialPort = SetupSerialPort(_serialPortSettings); 
     serialPort.Open(); 

     string data; 
     while (serialPort.IsOpen) 
     { 
      try 
      { 

       data = serialPort.ReadLine(); 
       if (data.Length > 0) 
        ReceivedData(serialPort, new ReceivedDataEventArgs(data)); 

      } 
      catch (TimeoutException) 
      { 
       // No action 
      } 
     } 
    } 
    catch (ThreadAbortException) 
    { 
     if (serialPort != null) 
      serialPort.Close(); 
    } 
} 

나는 "안전 손잡이는 폐쇄되었습니다"(코드없이 라인 또는 참조) 예외를 얻을. 아무도 내가 잘못하고있는 것을 발견 할 수 있습니까? 감사.

그건 그렇고, 나는 Start()Stop() 스레드를 생성하고 스레드를 중단합니다.

+0

일부 예제 코드를 추가했습니다. –

답변

10

스레드를 종료하기 위해 Thread.Abort를 사용하고 있기 때문에 이것이 의심 스럽습니다. 이 종료 될 때의 스레드 동작은을 예측할 수 없습니다. 따라서 직렬 포트는 원시 코드에 대한 래퍼이므로 .NET의 SafeHandle로 표시된 네이티브 리소스가 예기치 않게 삭제되어 Exception을 받게됩니다.

당신은이 같은 스레드에 일어나는 일에 대해 생각할 수 :

  • 당신이하여 SafeHandle을 기본 자원을 할당하고 사용하는 직렬 포트를 (열 스레드에게
  • 을 시작 (들)들에 잡아 당신은에 Thread.Abort를 호출) 스레드에 (예상치 못한
  • 당신이 어떤 점에서
  • 후 시리얼 포트에서 읽기 시작 자원)
  • 가장 가능성이 스레드의 코드는 그쪽에있다 t 포인트 (데이터를 읽을 수)
  • 스레드가 살해 도착 및 직렬 포트 핸들을 사용하면에서는 ReadLine (내부 코드에서 던진 예외가 암시 적으로
  • 을 파괴) 직렬 포트 인해 기능을 시리얼 포트에 액세스하려고 가지고 있던 핸들은 더 이상 유효하지 않습니다.

직렬 포트를 닫고 처분 할 적절한 기회를 얻으려면 실제로 스레드를 중단하는 다른 방법을 사용해야합니다. 당신이 에있는 스레드를 중지 할 이벤트 방법을 사용하는 경우

물론
protected ManualResetEvent threadStop = new ManualResetEvent(false); 

protected void ReadData() 
{ 
    SerialPort serialPort = null; 
    try 
    { 
     serialPort = SetupSerialPort(_serialPortSettings); 
     serialPort.Open(); 

     string data; 
     while (serialPort.IsOpen) 
     { 
      try 
      { 

       data = serialPort.ReadLine(); 
       if (data.Length > 0) 
        ReceivedData(serialPort, new ReceivedDataEventArgs(data)); 

      } 
      catch (TimeoutException) 
      { 
       // No action 
      } 

      // WaitOne(0) tests whether the event was set and returns TRUE 
      // if it was set and FALSE otherwise. 
      // The 0 tells the manual reset event to only check if it was set 
      // and return immediately, otherwise if the number is greater than 
      // 0 it will wait for that many milliseconds for the event to be set 
      // and only then return - effectively blocking your thread for that 
      // period of time 
      if (threadStop.WaitOne(0)) 
       break; 
     } 
    } 
    catch (Exception exc) 
    { 
     // you can do something here in case of an exception 
     // but a ThreadAbortedException should't be thrown any more if you 
     // stop using Thread.Abort and rely on the ManualResetEvent instead 
    } 
    finally 
    { 
     if (serialPort != null) 
      serialPort.Close(); 
    } 
} 

protected void Stop() 
{ 
    // Set the manual reset event to a "signaled" state --> will cause the 
    // WaitOne function to return TRUE 
    threadStop.Set(); 
} 

, 이벤트를 포함주의 :

스레드를 종료하는 적절한 방법은이 같은으로 ManualResetEvent를 사용하여 구현 될 수있다 장시간 실행되는 모든 루프 또는 작업에서 상태 검사를 수행하십시오.. 그렇게하지 않으면 스레드가 장시간 반복되는 루프 나 작업에서 벗어나 이벤트가 설정되었음을 "볼 수있는"기회가 생길 때까지 스레드가 설정 한 이벤트에 응답하지 않는 것처럼 보일 수 있습니다.

+0

내 스레드를 닫는 적절한 방법은 무엇이겠습니까? 'Stop()'/'Start()'는 포트를 재구성 할 때 사용됩니다. –

+0

당신이 할 수있는 몇 가지 방법이 있으며 꽤 일반적인 메커니즘 인 ManualResetEvent를 사용하는 예제를 제공했습니다. –

+0

Sweet. 고맙습니다! –

0

하나의 방법에 로컬로 직렬 포트 연결을 만들려고하는 유사한 상황으로 이동하십시오.

아이디어는 각 직렬 포트에 대해 최대 4 개의 serialPort 객체를 생성하는 것이 었습니다. 포트 중 하나가 좋은 데이터로 돌아 오면 내 장치가 연결된 포트를 알 수 있습니다. 내 "시험"개체를 모두 버리고 특정 COM 포트에 대한 새 연결을 만듭니다.

매번 방법을 통해 필자의 serialPort 개체를 만들거나 삭제합니다. 죄송합니다. 그래서 GC를 실행했을 때 다시 메서드를 호출하면 첫 번째 연결을 대체하는 두 번째 직렬 연결이 만들어지며 충돌 할 것입니다. 이 오류가 발생합니다.

해결책 : 모든 직렬 포트 연결 개체를 클래스에 대해 전역으로 만듭니다. 추악한 해킹 : 예,하지만 작동합니다.