两个.NET线程和硬件访问的问题

mac*_*369 8 c# multithreading hardware-interface

我正在创建一个通过FT2232H USB/RS232转换器与设备通信的应用程序.对于通信我正在使用FTDI网站上的FTD2XX_NET.dll库.
我正在使用两个线程:

  • 第一个线程连续从设备读取数据
  • 第二个线程是Windows窗体应用程序的主线程

    当我在接收器的线程运行时尝试将任何数据写入设备时,我遇到了问题.主线程只是挂起在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);
    
                }
            }
    
    Run Code Online (Sandbox Code Playgroud)


    从主线程调用的写函数

        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");
            }
        }
    
    Run Code Online (Sandbox Code Playgroud)


    用于同步两个线程的对象

    
    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);
    Run Code Online (Sandbox Code Playgroud)
  • Rog*_*ier 6

    另一种方法是使用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;
        }
    
    Run Code Online (Sandbox Code Playgroud)


    Dan*_*ant 4

    一些东西:

    1. 检查您的 Read 调用是否被阻塞。如果是这样,当 Read 阻塞等待响应时,您可能无法调用 Write。您的 API 文档可能对此有更多详细信息。

    2. 某些 API 不能很好地支持多线程,即使在同步访问时也是如此。如果是这种情况,您可以使用将写入命令委托给通信线程的设计。当我过去使用这种模式时,我通常会将某种包含我希望写入的信息的 Command 类排队,并使用线程 Signal 类来允许我调用“命令”方法来阻止或提供某种类型的命令。异步通知。