.NET SerialPort DataReceived事件线程干扰主线程

时间:2010-12-22 00:56:49

标签: c# .net serial-port

我正在使用C#中的SerialPort类编写串行通信程序,以便与通过RS232电缆连接的剥离机进行交互。当我将命令发送到机器时,它会根据命令以一些字节响应。就像我发送“\ D”命令一样,我希望将180字节的机器程序数据下载为连续字符串。根据机器的手册,它建议最好的做法是发送一个未识别的字符,如逗号(,)字符,以确保在循环中发送第一个命令之前初始化机器。我的串口通讯代码如下:

public class SerialHelper
{
    SerialPort commPort = null;
    string currentReceived = string.Empty;
    string receivedStr = string.Empty;
    private bool CommInitialized()
    {
        try
        {
            commPort = new SerialPort();
            commPort.PortName = "COM1";
            if (!commPort.IsOpen)
                commPort.Open();
            commPort.BaudRate = 9600;
            commPort.Parity = System.IO.Ports.Parity.None;
            commPort.StopBits = StopBits.One;
            commPort.DataBits = 8;
            commPort.RtsEnable = true;
            commPort.DtrEnable = true;

            commPort.DataReceived += new SerialDataReceivedEventHandler(commPort_DataReceived);
            return true;
        }
        catch (Exception ex)
        {
            return false;
        }
    }

    void commPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        SerialPort currentPort = (SerialPort)sender;
        currentReceived = currentPort.ReadExisting();
        receivedStr += currentReceived;
    }

    internal int CommIO(string outString, int outLen, ref string inBuffer, int inLen)
    {
        receivedStr = string.Empty;
        inBuffer = string.Empty;
        if (CommInitialized())
        {
            commPort.Write(outString);
        }

        System.Threading.Thread.Sleep(1500);

        int i = 0;
        while ((receivedStr.Length < inLen) && i < 10)
        {
            System.Threading.Thread.Sleep(500);
            i += 1;
        }

        if (!string.IsNullOrEmpty(receivedStr))
        {
            inBuffer = receivedStr;
        }
        commPort.Close();

        return inBuffer.Length;

    }
}

我从Windows窗体中调用此代码,如下所示:

len = SerialHelperObj.CommIO(",",1,ref inBuffer, 4)
len = SerialHelperObj.CommIO(",",1,ref inBuffer, 4)
If(inBuffer == "!?*O")
{
   len = SerialHelperObj.CommIO("\D",2,ref inBuffer, 180)
}

串口的有效返回值如下所示: \ D00000010000000000010 550 3250 0000256000等......

我得到这样的事情: \ D00000010D ,, 000 550 D ,,依旧......

当我发送命令时,我觉得我的通话调用被干扰了。但我试图确保逗号命令的结果然后启动实际命令。但是接收到的线程正在插入前一个通信周期中的字节。

任何人都可以对此有所了解......? 我试图让这项工作失去了一些头发。我不确定我在哪里做错了

3 个答案:

答案 0 :(得分:2)

串行端口不是线程安全的资源,也不是使用它们的包装器,也不是用于保存结果的字符串。发生的事情是DataReceived事件处理程序多次触发,并且您最终会同时执行多个处理程序。尝试向事件处理程序添加锁定块:

...

Object lockingObj = new Object();

...

void commPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    lock(lockingObj)
    {
        SerialPort currentPort = (SerialPort)sender;
        currentReceived = currentPort.ReadExisting();
        receivedStr += currentReceived;
    }
}

lock语句阻止多个线程一次执行块中的代码,因此receiveStr将获得一个ReadExisting的全部内容,然后再写入另一个。

请注意,这并不保证执行顺序;先进的胜利。所以,假设线程1首先到达此处,然后线程2在250ms后到达,而线程3在此之后250ms到达。线程1可能首先进入,执行Read需要一段时间,然后退出。在那段时间,线程2和3进入函数并在lock语句中被阻塞,等待线程1释放其锁定。一旦发生这种情况,由内核首先调度哪个线程,这可能是线程3,具体取决于许多操作系统和硬件因素。

答案 1 :(得分:0)

为什么每次都要重新初始化端口?我认为一个就足够了。

答案 2 :(得分:0)

我发现您的代码存在许多问题。

  1. 永远不要忽略异常。返回false忽略了异常。当你这样做时,你不知道发生了什么异常。
  2. 特别是,您的commPort对象可能永远不会被实例化。它可以处于任何状态,但是您忽略了异常并继续访问可能无效的对象。
  3. 当端口打开时,DataReceived事件可能随时发生。它可能会也可能不会在同一个线程上引发。您的代码可能正在从事件处理程序下关闭该端口,这会抛出您的代码未捕获的异常。
  4. 这里可能没关系,但你知道字符串连接在.NET中是一个相对昂贵的操作吗?字符串是不可变的。 receivedStr += currentReceived不会附加到receivedStr - 它会创建一个新的字符串对象来保存这两个部分。
相关问题