проблема с two.NET потоки и аппаратный доступ

Я создаю приложение, которое взаимодействует с устройством через конвертер FT2232H USB/RS232. Для связи я использую FTD2XX_NET.библиотека dll с сайта FTDI.
Я использую два потока:

  • первый поток непрерывно читает данные от прибора
  • второй поток является основным потоком приложения Windows Form

    У меня проблема, когда я пытаюсь записать любые данные на устройство во время работы потока приемника. Этот основной поток просто зависает на ftdiDevice.Функция write.

    Я попытался синхронизировать оба потока, чтобы только один поток мог одновременно использовать функцию чтения/записи, но это не помогло.

    Ниже код, ответственный за сообщение. Обратите внимание, что следующие функции являются методами класса FtdiPort.

    поток приемника

    
            private void receiverLoop()
            {
                if (this.DataReceivedHandler == null)
                {
                    throw new BackendException("dataReceived delegate is not set");
                }
    
                FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OK;
                byte[] readBytes = new byte[this.ReadBufferSize];
    
                while (true)
                {
                    lock (FtdiPort.threadLocker)
                    {
                        UInt32 numBytesRead = 0;
    
                        ftStatus = ftdiDevice.Read(readBytes, this.ReadBufferSize, ref numBytesRead);
    
                        if (ftStatus == FTDI.FT_STATUS.FT_OK)
                        {
                            this.DataReceivedHandler(readBytes, numBytesRead);
                        }
                        else
                        {
                            Trace.WriteLine(String.Format("Couldn't read data from ftdi: status {0}", ftStatus));
                            Thread.Sleep(10);
                        }                    
                    }
                    Thread.Sleep(this.RXThreadDelay);
    
                }
            }
    


    функция записи вызывается из main нить

    
    
    
        public void Write(byte[] data, int length)
        {
            if (this.IsOpened)
            {
                uint i = 0;
    
                lock (FtdiPort.threadLocker)
                {
                    this.ftdiDevice.Write(data, length, ref i);
                }
    
                Thread.Sleep(1);
                if (i != (int)length)
                {
                    throw new BackendException("Couldnt send all data");
                }
            }
            else
            {
                throw new BackendException("Port is closed");
            }
        }
    


    объект, используемый для синхронизации двух потоков

    
    static Object threadLocker = new Object();
    

    метод, который запускает поток приемника

    
            private void startReceiver()
            {
                if (this.DataReceivedHandler == null)
                {
                    return;
                }
                if (this.IsOpened == false)
                {
                    throw new BackendException("Trying to start listening for raw data while disconnected");
                }
                this.receiverThread = new Thread(this.receiverLoop);
                //this.receiverThread.Name = "protocolListener";
                this.receiverThread.IsBackground = true;
                this.receiverThread.Start();
            }
    
    В ftdiDevice.Функция Write не зависает, если я комментирую следующую строку:
    ftStatus = ftdiDevice.Read(readBytes, this.ReadBufferSize, ref numBytesRead);
  • 4 ответов


    несколько вещей:

    1. Проверьте, блокируется ли ваш вызов чтения. Если это так, вы не сможете вызвать Write, пока чтение блокирует ожидание ответа. Ваша документация API может иметь более подробную информацию об этом.

    2. некоторые API не поддерживают несколько потоков очень хорошо, даже при синхронизации доступа. Если это так, вы можете использовать дизайн, в котором вы делегируете свои команды записи в поток comm. Когда я использую это в прошлом я обычно выстраивал в очередь какой-то класс команд, содержащий информацию, которую я хочу написать, и либо использовал класс потокового сигнала, чтобы позволить моим вызывающим методам "command" блокировать или предоставлять какое-то асинхронное уведомление.


    альтернативой является использование механизма уведомления о событиях из FTDI, таким образом, вам не нужен блокирующий поток для считывания данных:

    public FTDISample()
    {
        private AutoResetEvent receivedDataEvent;
        private BackgroundWorker dataReceivedHandler;
        private FTDI ftdi;
    
        public FTDISample(string serialNumber){
            ftdi = new FTDI();
            FTDI.FT_STATUS status = ftdi.OpenBySerialNumber(serialNumber);
            receivedDataEvent = new AutoResetEvent(false);
            status = mFTDI.SetEventNotification(FTDI.FT_EVENTS.FT_EVENT_RXCHAR, receivedDataEvent);
            dataReceivedHandler = new BackgroundWorker();
            dataReceivedHandler.DoWork += ReadData;
            if (!dataReceivedHandler.IsBusy)
            {
                dataReceivedHandler.RunWorkerAsync();
            }
        }
    
        private void ReadData(object pSender, DoWorkEventArgs pEventArgs)
        {
            UInt32 nrOfBytesAvailable = 0;
            while (true)
            {
                // wait until event is fired
                this.receivedDataEvent.WaitOne();
    
                // try to recieve data now
                FTDI.FT_STATUS status = ftdi.GetRxBytesAvailable(ref nrOfBytesAvailable);
                if (status != FTDI.FT_STATUS.FT_OK)
                {
                    break;
                }
                if (nrOfBytesAvailable > 0)
                {
                    byte[] readData = new byte[nrOfBytesAvailable];
                    UInt32 numBytesRead = 0;
                    status = mFTDI.Read(readData, nrOfBytesAvailable, ref numBytesRead);
    
                    // invoke your own event handler for data received...
                    //InvokeCharacterReceivedEvent(fParsedData);
                }
            }
        }
    
        public bool Write(string data)
        {
            UInt32 numBytesWritten = 0;
            ASCIIEncoding enconding = new ASCIIEncoding();
            byte[] bytes = enconding.GetBytes(data);
            FTDI.FT_STATUS status = ftdi.Write(bytes, bytes.Length, ref numBytesWritten);
            if (status != FTDI.FT_STATUS.FT_OK)
            {
                Debug.WriteLine("FTDI Write Status ERROR: " + status);
                return false;
            }
            if (numBytesWritten < data.Length)
            {
                Debug.WriteLine("FTDI Write Length ERROR: " + status + " length " + data.Length +
                                " written " + numBytesWritten);
                return false;
            }
            return true;
        }
    

    Я нашел более подробную документацию API. Действительно, ftdiDevice.функция чтения блокируется, если вы не зададите значение readTimeout, отличное от 0. Установка этого значения тайм-аута решила проблему.
    Спасибо за быстрый ответ.
    С уважением


    проверяя API, мне кажется, что драйвер способен эмулировать COM-порт. Я вижу, что метод GetComPort () возвращает строку "COMx". Это делает весьма вероятным, что вы можете использовать систему.ИО.Порты.Класс SerialPort. Который уже делает то, что пытается сделать ваша оболочка, он поддерживает событие DataReceived. Стоит выстрел.