线程间串口通信

3
经过大量的研究,我仍然束手无策。我有一个串口对象,它在不断地读取数据。我能做到的是生成dataReceived事件、与端口通信并将收到的值输出到调试窗口。所以,我相信它在物理上全部正常工作。问题出现在当我尝试将串口输出传递给我的原始线程时,我会遇到错误。它说我不能有线程交叉通信(或类似这样的东西)。我一直尝试使用backgroundWorker,但我不确定那是否是我想要的解决方案,而且由于我的初学者水平,这略微超出了我的能力范围。我还尝试使用invoke,但该方法似乎不可用。 (可能我正在从错误的对象中调用?)无论如何,下面是相关代码片段。
namespace Photometer
{
    class csRadiometerILT1700
    {
        //manufacturer specs for baud rate, databits, and stop bits
        static string portName="COM1";
        static int baudRate = 1200;
        static int dataBits = 8;
        //instantialize a serial port object for the Radiometer
        private SerialPort RadiometerSerial = new SerialPort(portName, baudRate, Parity.None, dataBits, StopBits.One);


        //constructor
        //public csRadiometerILT1700(Form ParentForm, Chart outputChart)
        public csRadiometerILT1700()
        {


            //two handshaking properties of the ILT1700. Handshaking is enabled and 
            //http://stackoverflow.com/questions/6277619/problem-reading-serial-port-c-net-2-0-to-get-weighing-machine-output
            RadiometerSerial.Handshake= Handshake.RequestToSend;
            RadiometerSerial.DtrEnable = true;

            RadiometerSerial.DataReceived += new SerialDataReceivedEventHandler(RadiometerSerial_DataReceived);

        }

        public void openPort()
        {
            if (!RadiometerSerial.IsOpen)
            {
               RadiometerSerial.Open(); 
            }

        }
        public void closePort()
        {
            RadiometerSerial.Close();
        }

        string RadiometerVoltageReadingString;
        int RadiometerVoltageReadingInt;
        private void RadiometerSerial_DataReceived(object sender, SerialDataReceivedEventArgs e) 
        {
            //It's here that this.invoke()... cannot be called.

            RadiometerVoltageReadingString= (RadiometerSerial.ReadExisting().ToString());    //y-value
            Debug.Print(RadiometerVoltageReadingString.ToString());

            makeRadioReadingDouble(RadiometerVoltageReadingString);

        }

        private void makeRadioReadingDouble(string inputVoltageString)
        {

            List<double> outputVoltageDouble=new List<double>(2);


            if (!(inputVoltageString == "\r\n" || inputVoltageString == ""))
            {
                string[] voltageValAndExpo = inputVoltageString.Split(new string[] { "e", "\r\n" }, StringSplitOptions.RemoveEmptyEntries);

                for (int inCounter = 0; inCounter < voltageValAndExpo.Count(); inCounter=inCounter+2)
                {
                    double voltageVal = Convert.ToDouble(voltageValAndExpo[inCounter]);
                    double voltageExpo = Convert.ToDouble(voltageValAndExpo[inCounter + 1]);

                    outputVoltageDouble.Add(Math.Pow(voltageVal, voltageExpo));
                }
            }
        }

    }
}

当我使用代码加载表单时,所有这些都被称为“调用”。
            csRadiometerILT1700 Radiometer;

            ...

            Radiometer = new csRadiometerILT1700();
            Radiometer.openPort();

任何见解都受到欢迎。
编辑: 我改变了我的csRadiometerILT1700构造函数为
   public csRadiometerILT1700(Form inputForm)
    {

        //inputForm.Invoke(
        //two handshaking properties of the ILT1700. Handshaking is enabled and 
        //http://stackoverflow.com/questions/6277619/problem-reading-serial-port-c-net-2-0-to-get-weighing-machine-output
        RadiometerSerial.Handshake= Handshake.RequestToSend;
        RadiometerSerial.DtrEnable = true;

        RadiometerSerial.DataReceived += new SerialDataReceivedEventHandler(RadiometerSerial_DataReceived);
        inputForm.Invoke(DataReceived);
    }

并声明
public event Delegate DataReceived;

在csRadiometerILT1700类中,我遇到了“数据接收必须是委托类型”的错误。现在该如何解决?我的做法正确吗?


不要在构造函数中调用。这里有一个设置事件的链接:https://dev59.com/8XE95IYBdhLWcg3wHqMV#2448530 - H H
你的类应该在接收到有意义的数据时引发其事件,并将其打包到一个派生自EventArgs的类中。表单在其处理程序中订阅(+=)并调用它。 - H H
5个回答

2
  1. 您的RadiometerILT1700类需要一个事件来报告它接收(和处理)的数据。
  2. 您的表单订阅该事件。
  3. 表单的事件处理程序使用this.Invoke()来解决跨线程问题。

  1. 这个事件会在DataReceived事件中被触发吗?当我处理完X量的串行数据后,我会标记这个事件吗?
  2. 我该如何从调用表单订阅它?
  3. 这是否意味着我将“订阅”放在Invoke()中?
- Brad
  1. 是的。
  2. 使用 +=,就像其他事件一样。
  3. 不需要,您可以从已订阅的表单方法中调用。可以调用另一个方法或同一个方法。
- H H
对不起,我还是有点迷惑。也许需要更多的指导。您能否使用更多的词语来描述与2)相关的操作? - Brad

1

InvokeDelegateFormControl 上的一个方法,由于 csRadiometerILT1700 不属于这些类之一,因此它没有从这些类中继承 Invoke 实现。

您需要向 csRadiometerILT1700 的调用者引发另一个事件,并在 GUI 中处理它(以及任何跨线程问题)。或者,您可以为 csRadiometerILT1700 提供一个委托,它可以用来回调,就像手动编写的事件一样。

一旦您在 Form 中拥有数据,您可以使用 Control.InokeRequired 来检测跨线程情况,并使用 Control.Invoke 进行跨线程调用。


我觉得我对委托语法有些困惑。在调用表单中应该放哪些部分?而在处理事件的类中又应该放哪些部分呢? - Brad
“Delegate”是函数签名的定义。 “Delegate”或“MultiCastDelegate”是表示“delegate”实例的类。 “Event”是指定“delegate”的“MulticastDelegate”,并提供了“raising”或“invoking”调用的快捷方式。 - Jodrell
Delegate可以在任何地方声明,而event应该在想要引发它的类上声明。一个与之匹配且用于订阅事件MulticastDelegate的函数应该在想要处理事件的类上。 - Jodrell
我最终只是设置了一个500毫秒的计时器,并在串行端口上执行了一个readLine()操作。这是它可以发送消息而不会出现停顿的最高频率。事件和中断留待以后再处理。感谢您的帮助。 - Brad

0

如果你试图从不同的线程向主线程发布内容,就会出现这个错误。你需要使用委托并在不是主窗体线程的线程上调用控件。


0

假设错误实际上是:

从创建它的线程以外的线程访问控件控件名称。

问题在于您无法从另一个线程影响GUI。您需要invoke表单或控件来传递控制权,以便可以修改它。您应该能够与大多数其他元素交互,但不能与表单交互。


0

你不应该在那里调用 this.Invoke,你现在的做法就像是试图将自己的线程与 UI 线程同步,而不是与 UI 同步。你应该调用 formhandle.Invoke,或者在表单类中注册事件,然后你可以使用 this.invoke


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接