C#多个串行端口同时读取

C#多个串行端口同时读取,c#,multithreading,serial-port,C#,Multithreading,Serial Port,再次返回到另一个串行端口问题 在过去的两周里,我一直在改进我的代码,以尝试读取两个串行设备。目前,该程序获得了几乎所有的数据(大约99%,而我以前只有50%),但我仍然缺少数据 以下是我的“主要”功能: private Program() { port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived); port2.DataReceived += new Serial

再次返回到另一个串行端口问题

在过去的两周里,我一直在改进我的代码,以尝试读取两个串行设备。目前,该程序获得了几乎所有的数据(大约99%,而我以前只有50%),但我仍然缺少数据

以下是我的“主要”功能:

private Program()
    {
        port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
        port2.DataReceived += new SerialDataReceivedEventHandler(port2_DataReceived);


        port.Open();
        port2.Open();


        Application.Run();
    }
以下是我用于两个串行端口的代码:

public void port1IntoBuffer()
    {
        int messageLength = 96; 
        byte[] buffer = new byte[messageLength];
        port.BaseStream.ReadAsync(buffer, 0, messageLength);

        for (int i = 0; i < messageLength; i++)
        {
            if ((int)buffer[i] <= 48 && (int)buffer[i] > 0)
            {
                tickQ.Enqueue((new IdDate { Id = (int)buffer[i], Date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") }));
            }
        }
        if (!Locked)
        {
            Locked = true;
            Thread readThread = new Thread(() => submitSQL());
            readThread.Start();
        }
    }
现在我的问题是,10分钟后,我对这个程序的RAM使用量激增(从6MB增加到150MB),然后程序崩溃


编辑2:在大家的推荐下,结果有所改善!昨晚我只错过了大约15000次传输中的8次。我将尝试增加串行读取缓冲区,希望能够捕获更多的传输。

我注意到您的代码中有几个问题,第一个问题是我作为注释处理的函数调用是异步的,这意味着在后台获取请求的数据时,它将提前返回。

通常,您可以通过在返回的
任务
上发出
wait
来处理此问题,表示您希望在请求的字节量完全读取后恢复代码(而不阻塞当前线程)

wait
仅在C#5之后可用。如果在较低版本上运行,则通常会安排一个延续,这是一种将在任务完成后运行的委托。但是,我认为只要切换到
Read()
版本就可以了,线程在等待数据时肯定会阻塞,但至少您可以确定您已经读取了预期的字节数

我注意到的另一个问题是您的锁定代码依赖于布尔标志,它不是线程安全的。问题在于,检查
锁定
标志状态以有条件进入受保护块的代码,以及实际设置块内标志状态的代码不是原子代码这意味着另一个线程可能会输入您试图保护的代码,而另一个线程也输入了相同的代码块,但仍然无法将保护标志切换为true,在这种情况下,两个线程将运行相同的代码,可能会产生意外的结果(不确定这种可能性是否与您丢失的数据问题有关)

如果不查看
lock
成员的其他使用方式和释放方式,我真的无法推荐如何重写锁防护代码。如果您发布与
lock
成员交互的其余代码片段,我可能可以提供一些建议

编辑:(OP评论后的更多信息)

该标志的目的是防止每个线程都发布到 立即打开数据库(我不断收到死锁错误,但我没有 我想这会导致我的数据丢失)

有了这些信息,我认为您可能会遇到另一个问题,这肯定可以解释丢失的数据。如果标志为true,会发生什么情况?您只需跳过启动将数据提交到SQL server的线程,就永远不会重试,因此一旦方法退出,缓冲数据就会丢失。很抱歉,您不会丢失数据,因为如果您在等待它,您可能会损失实际耗尽数据接收队列所需的线程启动量。当您遇到数据丢失时,请检查您的
tickQ
队列,我打赌它仍将有数据要处理,因为本来应该处理它的线程从未因为同样的原因启动过

submitSQL()
函数中添加
lock()
语句将保护您免受这种可能性的影响

从代码中完全删除锁定的成员,然后尝试以下操作:

// Add this member to your class;
object SqlLock = new object();



    void submitSQL() {
      // add this at the start of your submitSQL() method
       lock (SqlLock ) {
         ... the rest of your code
       }
    }
与当前代码的不同之处在于,如果SQL实例正由另一个线程使用,则调用线程将在继续之前阻止并等待它可用,而不是简单地丢弃刚刚读取的数据。不启动处理排队数据的线程

编辑:最后一条建议

我建议您不要每次需要耗尽队列时都创建新线程,相反,您可以依赖线程池(请参见
Task.Run()
)。使用池中的线程可以消除手动创建线程的昂贵开销


另一种可能是创建一个将持续专用于排空数据队列的单个线程。您可以使用
AutoResetEvent
来协调生产者线程(排队的线程)和消费者线程(排队的线程)之间的工作您的使用者线程进入一个调用
AutoResetEvent.WaitOne()
的循环,在该循环中它将阻塞;您的生产者线程将接收到的数据排队,而不是生成一个新线程,它调用
AutoResetEvent.Set()
导致使用者线程唤醒并处理排队的数据,一旦处理完毕,它将再次阻塞,等待下一批数据到达。如果这样做,请确保将线程标记为
背景线程
,这样,如果线程正在等待数据,则不会阻止应用程序关闭。

您可以对预期数据是什么以及您当前获得的数据进行评级?我的预期数据是一个介于1-48之间的整数值,我会记录何时收到该值(使用My DateTime.Now())。一旦我有两个相同整数值的数据点,我就减去到达时间以获得持续时间。我的问题是,我偶尔会错过一次转换,使我的持续时间比它应该的要长得多。我正在将我的结果与现有系统进行比较(我正在编写的程序将取代它).我的程序通常关闭5或6次读取(意思是
// Add this member to your class;
object SqlLock = new object();



    void submitSQL() {
      // add this at the start of your submitSQL() method
       lock (SqlLock ) {
         ... the rest of your code
       }
    }