2009-08-23 6 views
7

Bonjour tout le monde!"Safe handle a été fermé" avec SerialPort et un fil en C#

J'ai ce wrapper fileté SerialPort qui lit dans une ligne du port série. Voici le code de mon fil.

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(); 
    } 
} 

quand je l'appelle myThread.Abort(); je reçois une exception (sans ligne ou référence au code) « poignée Safe a été fermé ». Quelqu'un peut-il repérer ce que je fais mal? Merci. En passant, j'ai un Start() et un Stop() qui crée le fil et abandonne le fil, respectueusement.

+0

J'ai ajouté un exemple de code –

Répondre

10

Je suppose que c'est parce que vous utilisez Thread.Abort pour terminer le thread - ce qui est généralement mal vu. Le comportement du thread lorsque vous abandonner il n'est pas prévisible. Pour cette raison, étant donné que le port série est un wrapper sur du code natif, il existe des ressources natives - représentées par un SafeHandle dans .NET - qui sont éliminées de manière inattendue et vous obtenez ainsi l'exception.

Vous pouvez penser à ce qui se passe avec votre fil comme ceci:

  • vous démarrez votre fil
  • vous ouvrez le port série (qui alloue les ressources indigènes et utilise SafeHandle (s) pour conserver les ressources)
  • vous commencez à lire à partir du port série
  • puis à un moment donné (inattendu à votre fil) vous appelez Thread.Abort sur elle
  • très probablement le code dans votre fil est à tha point t essayer d'accéder au port série (pour lire les données)
  • le fil est tué et la poignée de port série est détruit implicitement
  • vous obtenez une exception levée à partir du code dans la fonction ReadLine() du port série, car le handle dont il disposait n'est plus valide

Vous devriez vraiment utiliser une méthode différente pour abandonner le thread afin d'avoir une chance de fermer et d'éliminer le port série.

Une bonne façon de fermer votre fil pourrait être mis en œuvre en utilisant un ManualResetEvent comme ceci:

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(); 
} 

Bien sûr, lorsque vous utilisez la méthode des événements pour arrêter le fil que vous devez être pris soin d'inclure un événement état vérifier dans toutes vos longues boucles ou tâches en cours. Si vous ne voyez pas votre thread peut sembler ne pas répondre à votre réglage de l'événement - jusqu'à ce qu'il sorte de la boucle de longue durée, ou une tâche et obtient une chance de "voir" que l'événement a été défini.

+0

Quelle serait la bonne façon de fermer mon fil. Les 'Stop()'/'Start()' sont utilisés lorsque je reconfigure le port. –

+0

Il y a plusieurs façons de le faire et j'ai donné un exemple en utilisant un ManualResetEvent qui est un mécanisme assez commun .. –

+0

Sweet. Je vous remercie! –

0

Ran dans une situation similaire où j'ai tenté de créer des connexions de port série locales à une seule méthode.

L'idée était de créer jusqu'à quatre objets serialPort (pour chaque port série). Une fois que l'un des ports est revenu avec de bonnes données, je savais à quel port mon appareil était connecté. Je supprimerais tous mes objets "d'essai" et créerais une nouvelle connexion au port de com spécifique.

Chaque fois que la méthode est utilisée, je crée/supprime mes objets serialPort. Oops. Il se trouve que si le GC n'avait pas fonctionné au moment où j'ai appelé à nouveau la méthode, il tenterait de créer une seconde connexion série en remplaçant la première connexion et ils entreraient en collision. Cela déclencherait cette erreur.

Solution: rendre tous les objets de connexion de port série globaux à la classe. Ugly Hack: Oui, mais ça marche

Questions connexes