2012-01-12 8 views
5

Mam program WinForm, który wykonuje niektóre asynchroniczne IO na SerialPort. Jednak od czasu do czasu napotykam na problem z zamrożeniem programu w wywołaniu SerialPort.Close(), pozornie losowym.C# Winmost zamrażanie na SerialPort.Close

Myślę, że to kwestia bezpieczeństwa wątków, ale nie jestem pewien, jak to naprawić, jeśli tak jest. Próbowałem dodać/usunąć asynchroniczną procedurę obsługi DataReceived z funkcjami otwierania/zamykania portów i odrzucania buforów wejściowych i wyjściowych na porcie, ale nie wydaje się, aby to działało. Myślę, że ważny kod SerialPort jest poniżej:

using System; 
using System.Collections.Generic; 
using System.IO.Ports; 

public class SerialComm 
{ 
    private object locker = new object(); 

    private SerialPort port; 
    private List<byte> receivedBytes; 

    public SerialComm(string portName) 
    { 
    port = new SerialPort(portName); 
    port.BaudRate = 57600; 
    port.Parity = Parity.None; 
    port.DataBits = 8; 
    port.StopBits = StopBits.One; 

    receivedBytes = new List<byte>(); 
    } 

    public void OpenPort() 
    { 
    if(port!=null && !port.IsOpen){ 
     lock(locker){ 
     receivedBytes.Clear(); 
     } 

     port.DataReceived += port_DataReceived; 
     port.Open(); 
    } 
    } 

    public void ClosePort() 
    { 
    if(port!=null && port.IsOpen){ 
     port.DataReceived -= port_DataReceived; 
     while(!(port.BytesToRead==0 && port.BytesToWrite==0)){ 
     port.DiscardInBuffer(); 
     port.DiscardOutBuffer(); 
     } 
     port.Close(); 
    } 
    } 

    private void port_DataReceived(object sender, SerialDataReceivedEventArgs e) 
    { 
    try{ 
     byte[] buffer = new byte[port.BytesToRead]; 
     int rcvdBytes = port.Read(buffer, 0, buffer.Length); 

     lock(locker){ 
     receivedBytes.AddRange(buffer); 
     } 

     //Do the more interesting handling of the receivedBytes list here. 

    } catch (Exception ex) { 
     System.Diagnostics.Debug.WriteLine(ex.ToString()); 
     //put other, more interesting error handling here. 
    } 
    } 
} 

UPDATE

Dzięki @ odpowiedź Afrin za wskazując stan zakleszczenia z wątku UI (This blog post ma dobrą pracę opisując ją i daje kilka innych dobre wskazówki), dokonałem prostej zmiany i nie udało się jeszcze odtworzyć błędu!

private void port_DataReceived(object sender, SerialDataReceivedEventArgs e) 
{ 
    try{ 
    byte[] buffer = new byte[port.BytesToRead]; 
    int rcvdBytes = port.Read(buffer, 0, buffer.Length); 

    lock(locker){ 
     receivedBytes.AddRange(buffer); 
    } 

    ThreadPool.QueueUserWorkItem(handleReceivedBytes); 

    } catch (Exception ex) { 
    System.Diagnostics.Debug.WriteLine(ex.ToString()); 
    //put other, more interesting error handling here. 
    } 
} 

private void handleReceivedBytes(object state) 
{ 
    //Do the more interesting handling of the receivedBytes list here. 
} 

Odpowiedz

13

Powodem byłoby powiesić po zamknięciu to dlatego, że w obsługi zdarzeń swojej SerialPort obiektu

Ty synchronizacji połączenia z głównym wątku (zazwyczaj poprzez wywołanie wywołać). Metoda zamykania SerialPort czeka na wątek EventLoopRunner, który uruchamia zdarzenia DataReceived/Error/PinChanged, aby zakończyć. ale ponieważ twój własny kod w wydarzeniu również czeka na odpowiedź głównego wątku, natrafiasz na sytuację martwego zamka.

rozwiązanie: zamiast użyć BeginInvoke powołaniem: https://connect.microsoft.com/VisualStudio/feedback/details/202137/serialport-close-hangs-the-application

referencyjny: http://stackoverflow.com/a/3176959/146622

+0

Aby upewnić się, ja cię rozumiem poprawnie, w skrócie, jest to impas w 'SerialPort' pomiędzy' Read' a Wywołania "Zamknij"? – chezy525

+0

musisz zmienić sposób wywoływania aktualizacji elementów interfejsu użytkownika w procedurze obsługi zdarzeń port_DataReceived, użyć BeginInvoke do aktualizacji zamiast Invoke lub zgodnie z opisem w rozwiązaniu użyć innego wątku do obsługi zdarzenia. – Afshin

+0

Wygląda na to, że obsługa danych dla interfejsu w innym wątku rozwiązała problem. Dzięki! – chezy525