«Безопасный дескриптор был закрыт» с SerialPort и потоком в C # - PullRequest
7 голосов
/ 23 августа 2009

Добрый день всем!

У меня есть эта многопоточная оболочка SerialPort, которая читает строку из последовательного порта. Вот код моей ветки.

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

когда я звоню myThread.Abort();, я получаю исключение (без строки или ссылки на код) "Безопасный дескриптор закрыт". Кто-нибудь может определить, что я делаю не так? Спасибо.

Кстати, у меня есть Start() и Stop(), которые создают поток и соответственно прерывают поток.

Ответы [ 2 ]

10 голосов
/ 23 августа 2009

Я подозреваю, что это потому, что вы используете Thread.Abort для завершения потока - что обычно вызывает недовольство. Поведение потока при прерывании не предсказуемо. Из-за этого, поскольку последовательный порт является оболочкой над собственным кодом, существуют собственные ресурсы, представленные SafeHandle в .NET, которые неожиданно удаляются, и вы получаете исключение.

Вы можете думать о том, что происходит с вашей темой, как это:

  • Вы начинаете свою тему
  • вы открываете последовательный порт (который выделяет собственные ресурсы и использует SafeHandle (s) для удержания этих ресурсов)
  • вы начинаете читать с последовательного порта
  • затем в какой-то момент (неожиданно для вашего потока) вы вызываете Thread.Abort для него
  • Скорее всего, код в вашем потоке в этот момент пытается получить доступ к последовательному порту (для чтения данных)
  • поток уничтожается, а дескриптор последовательного порта неявно уничтожается
  • вы получаете исключение из кода внутри функции ReadLine () последовательного порта, потому что дескриптор, который он имел, больше не действителен

Вы действительно должны использовать другой метод для прерывания потока, чтобы у вас была возможность закрыть и удалить последовательный порт.

Правильный способ закрыть ваш поток может быть реализован с помощью ManualResetEvent, например:

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

Конечно, при использовании метода событий, чтобы остановить поток, вы должны быть осторожным, чтобы включить проверку состояния события во все ваши длинные циклы или задачи . Если вы этого не сделаете, ваш поток может не реагировать на ваши установки события - пока он не выйдет из долгосрочного цикла или задачи и не получит возможность «увидеть», что событие установлено.

0 голосов
/ 22 декабря 2009

Столкнулся с аналогичной ситуацией, когда я пытался создать последовательный порт, локальный для одного метода.

Идея заключалась в создании до четырех объектов serialPort (для каждого последовательного порта). Как только один из портов вернулся с хорошими данными, я бы знал, к какому порту подключено мое устройство. Я бы отбросил все свои «пробные» объекты и создал бы новое соединение с конкретным com-портом.

Каждый раз, когда я использую метод, я создаю / отбрасываю свои объекты serialPort. К сожалению. Бывает, что если к тому времени, когда я снова вызвал метод, GC не запустится, он попытается создать второе последовательное соединение, заменив первое, и они столкнутся. Это вызвало бы эту ошибку.

Решение: сделать все объекты подключения последовательного порта глобальными для класса. Гадкий Хак: Да, но это Работает

...