2012-01-12 18 views
5

Possiedo un programma Winform che esegue un IO asincrono su un SerialPort. Tuttavia, sto periodicamente incontrando un problema con il blocco del programma sulla chiamata SerialPort.Close(), apparentemente a caso.C# Winform si blocca su SerialPort.Close

Penso che sia un problema di sicurezza dei thread, ma non sono sicuro di come risolverlo se lo è. Ho provato ad aggiungere/rimuovere il gestore DataReceived asincrono con le funzioni di apertura/chiusura della porta e scartare i buffer in e out sulla porta, ma non sembra fare nulla. Credo che il codice importante SerialPort è qui sotto:

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

Grazie a @ risposta di Afrin sottolineando la condizione di stallo con il thread UI (This blog post fa un buon lavoro che descrive, e dà molti altri buoni consigli), ho apportato un semplice cambiamento e non sono ancora riuscito a riprodurre l'errore!

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. 
} 

risposta

13

Il motivo sarebbe bloccarsi quando si chiude è perché nel gestore di eventi del vostro oggetto SerialPort

Stai sincronizzazione una chiamata con il thread principale (in genere chiamando richiamare). Il metodo close di SerialPort attende il thread EventLoopRunner che attiva gli eventi DataReceived/Error/PinChanged per terminare. ma dal momento che il proprio codice nell'evento attende anche che il thread principale risponda, si verifica una situazione di blocco morto.

soluzione: utilizzare BeginInvoke invece di invoke: https://connect.microsoft.com/VisualStudio/feedback/details/202137/serialport-close-hangs-the-application

di riferimento: http://stackoverflow.com/a/3176959/146622

+0

Per assicurarsi ho capito bene, in sintesi, c'è una situazione di stallo all'interno della 'SerialPort' tra il' Read' e la Chiamate 'Close'? – chezy525

+0

è necessario modificare il modo in cui si richiamano gli aggiornamenti degli elementi dell'interfaccia utente nel gestore eventi port_DataReceived, utilizzare BeginInvoke per aggiornare anziché Invoke oppure, come descritto nella soluzione, utilizzare un altro thread per gestire l'evento. – Afshin

+0

La gestione dei dati per l'interfaccia utente su un thread diverso sembra aver risolto il problema. Grazie! – chezy525