2009-06-10 3 views
3

직렬 포트 (예 : COM1)에서 데이터를 지속적으로 읽어야하는 일부 SerialPort 코드가 있습니다. 그러나 이것은 매우 CPU 집중적 인 것으로 보이며 사용자가 창을 이동하거나 많은 데이터가 창에 표시되는 경우 (예 : 직렬 회선을 통해 수신되는 바이트) 통신이 엉망이됩니다.System.IO.Ports.SerialPort 및 Multithreading

다음 코드를 고려 : 데이터 스트림 상수 및 데이터 (프레임/패킷)이 분석되어야로

void port_DataReceived(object sender, SerialDataReceivedEventArgs e) 
{ 

byte[] buffer = new byte[port.ReadBufferSize]; 

var count = 0; 

try 
{ 
    count = port.Read(buffer, 0, buffer.Length); 
} 
catch (Exception ex) 
{ 
    Console.Write(ex.ToString()); 
} 

if (count == 0) 
    return; 

//Pass the data to the IDataCollector, if response != null an entire frame has been received 


var response = collector.Collect(buffer.GetSubByteArray(0, count)); 

if (response != null) 
{ 
    this.OnDataReceived(response); 
} 

코드를 수집 할 필요가있다.

port = new SerialPort(); 

    //Port configuration code here... 

    this.collector = dataCollector; 

    //Event handlers 
    port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived); 
    port.Open(); 

가 사용자 상호 작용이없고 아무 것도 창에 추가되고 있지 않으면 이 잘 작동하지만, 곧 상호 작용 통신이 있으므로 정말 엉망 가져옵니다.

Dispatcher.BeginInvoke(new Action(() => 
{ 
    var builder = new StringBuilder(); 
    foreach (var r in data) 
    { 
     builder.AppendFormat("0x{0:X} ", r); 
    } 


    builder.Append("\n\n"); 

    txtHexDump.AppendText(builder.ToString()); 

    txtHexDump.ScrollToEnd(); 


}),System.Windows.Threading.DispatcherPriority.ContextIdle); 
}); 

을하지만, 심지어 간단한 통화는 문제를 일으킬 log4net하기 : 제한 시간은 예를 들어

.... 등이 발생,이 모든 것을 망쳐 놨어요.

이 경우 위는하지 않았다 많은 sence : ...

업데이트

는하여 SerialPort 통신 을 최적화 할 수있는 모범 사례가 있습니까 또는 누군가가 내가 잘못 뭘하는지 말해 줄 수 .

class Program 
{ 
    static void Main(string[] args) 
    { 
     var server = new BackgroundWorker(); 
     server.DoWork += new DoWorkEventHandler(server_DoWork); 
     server.RunWorkerAsync(); 

     var port = new SerialPort(); 
     port.PortName = "COM2"; 
     port.Open(); 
     string input = ""; 

     Console.WriteLine("Client on COM2: {0}", Thread.CurrentThread.ManagedThreadId); 
     while (input != "/quit") 
     { 
      input = Console.ReadLine(); 
      if (input != "/quit") 
      { 
       var data = ASCIIEncoding.ASCII.GetBytes(input); 
       port.Write(data, 0, data.Length); 
      } 
     } 

     port.Close(); 
     port.Dispose(); 
    } 

    static void server_DoWork(object sender, DoWorkEventArgs e) 
    { 
     Console.WriteLine("Listening on COM1: {0}", Thread.CurrentThread.ManagedThreadId); 
     var port = new SerialPort(); 
     port.PortName = "COM1"; 
     port.Open(); 

     port.ReceivedBytesThreshold = 15; 
     port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived); 
    } 

    static void port_DataReceived(object sender, SerialDataReceivedEventArgs e) 
    { 
     var port = (SerialPort)sender; 
     int count = 0; 
     byte[] buffer = new byte[port.ReadBufferSize]; 
     count = ((SerialPort)sender).Read(buffer, 0, buffer.Length); 

     string echo = ASCIIEncoding.ASCII.GetString(buffer,0,count); 
     Console.WriteLine("-->{1} {0}", echo, Thread.CurrentThread.ManagedThreadId); 
    } 
} 
결과는 다음과 같을 수

:

이 COM1에 듣기 : 6 클라이언트를 COM2에 10 이 내가 보내는 일부 샘플 데이터가 난 아주 간단한 (바보) 작은 예를했다 ---> 6 이것은 내가 주 스레드에서 발생

그래서 포트에서 데이터를 읽는 보내 일부 샘플 데이터 ....

이 내 문제를 일으키는 무엇의 일부가 될 수 있음은?

또한
private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) 
{ 
     var port = (SerialPort)sender; 
     while (port.BytesToRead > 0) 
     { 
      int byte_count = port.BytesToRead; 
      byte[] buffer = new byte[byte_count]; 

      int read_count = port.Read(buffer, 0, byte_count); 

      // PROCESS DATA HERE 

     } 
} 

난 그냥 절차 port_DataReceived의 큐 목록에서 데이터를 삽입하도록 권장하고, 데이터를 수행 할 것입니다 :

+0

코드의 마지막 섹션은 어디에 있습니까? '데이터'변수의 출처는 어디입니까? –

+0

위 코드는 IDataCollector가 이벤트를 발생시켜 전체 데이터 프레임을 수집했음을 나타낼 때 호출됩니다. WPF Windows에 있습니다. 그러나 IDataTransportServer 또는 IDataCollector 구현 코드에서 log.Warn (".....") 문을 추가하더라도 SerialCommunication은 엉망이됩니다. '데이터'는 단순히 직렬 줄 번호 : buffer.GetSubByteArray (0, count) – TimothyP

+0

그냥 어떤 코드라도 직렬 통신이 잘못 될 수 있습니다 ... – TimothyP

답변

4

Windows 응용 프로그램의 경우 주 스레드에서 이벤트가 실행된다는 마지막 결론이 적용될 수 있습니다. 콘솔에서 이것을 테스트하지 마십시오.

조정이 적절한 방법이다 :

  • 최소 4,096 일반적

  • 오클라호마 (허용 할만큼 높은 ReceivedBytesThreshold 설정되어 있지만, 충분히 큰 버퍼를 설정하기 전에 을 그것을 the Open())

  • 은 Received 이벤트에서 가능한 한 작게합니다. 더 많은 ti가 필요하면 데이터를 Queue 또는 MemoryStream에 전달하십시오. me

2

당신은 port.BytesToRead이 같은 제로보다 큰 때까지 데이터를 읽을 port_DataReceived에게 절차를 다시 작성해야 별도의 스레드에서 처리.

+0

내가 그렇게하면 어떤 시점에서 그냥 기다립니다 ... – TimothyP

+0

inside port_DataReceived() 함수 또는 port_DataReceived() 호출되지 않습니다? 원래 게시물에 쓰지 않은 중요한 것은 거의 없습니다. - port_DataReceived는 콜백 함수이며 자체 스레드에서 실행됩니다. COM 포트에 기록 된 데이터가있을 때마다 호출됩니다. 그러나 새 데이터가 COM 포트에 쓰여지고 이전 호출에서 반환되지 않았기 때문에 port_DataReceived 함수 호출이 잘못 될 수 있습니다. 즉, 함수 내에서 데이터를 처리합니다. 즉, 전에 port.BytesToRead 속성을 확인하는 것이 중요합니다. 기능을 떠난다. –

1

고전적인 해결책은 FIFO 버퍼를 갖는 것입니다. FIFO의 크기가 많은 입력이 있고 프로세서 블록이 차지하는 중요한 경우를 처리 할 수있을만큼 충분히 큰 지 확인하십시오.

--->|Reader|-->FIFO-->|Processor|--->FIFO2--->|Displayer| 
11

나는 아무도이 잡은하지 놀란다 :

는 당신은 2 버퍼 시스템을 가질 수 있습니다. SerialPort 클래스는 DataReceived 이벤트를 사용할 때 자체 스레드를 사용합니다. 즉, 예를 들어 구독자가 Form 요소에 액세스하는 경우 Invoke 또는 BeginInvoke 메서드를 사용하여이를 수행해야합니다. 그렇지 않으면 크로스 스레드 작업이 끝납니다. 이전 버전의 .net에서는 예측할 수없는 동작 (PC의 CPU 코어에 따라 다름)이 눈에 띄지 않게되고 이후 버전에서는 예외가 발생합니다.

+0

+1,이 질문에 대한 대답을 확신 할 수는 없지만 과거에는 조금 물었다는 중요한 정보입니다. – Brad