【问题标题】:.NET CORE(on windows), DataReceived called frequency of one serialPort effected by other serialPort.NET CORE(在 Windows 上),DataReceived 调用一个serialPort 的频率受其他serialPort 影响
【发布时间】:2019-04-29 16:06:09
【问题描述】:

我有一个 .NET CORE 2.1 控制台应用程序,它通过 2 个 comPort (rs232) 与 2 个都是 half-duplex 的设备进行通信。

  • COM1 上的设备 A,波特率 9600,我的应用每 200 毫秒轮询一次,并在 50 毫秒内得到响应。
  • COM2 上的设备 B,波特率 1200,我的应用每 400 毫秒接收一次轮询,并在 50 毫秒内响应。

两个comPort的代码完全分开,没有共享变量,没有引用等。

对于设备 A:

private ConcurrentQueue<object> outgoingMessageQueue = new ConcurrentQueue<object>();
this.comPort1.DataReceived += (a,b)=>{
    while (this.comPort1.BytesToRead > 0){
        var b = this.comPort.ReadByte();
        buffer.Add((byte)b);
    };
    if(CheckIsFullMessage(buffer))
    {//fire event for consume}

};
ThreadPool.QueueWorkerThread((_)=>{
while(true){
    Thread.Sleep(200); 
    if (this.outgoingMessageQueue.TryDequeue(out object pendingForWrite))
    {this.comPort1.Write(pendingForWrite); }
    else
        this.comPort1.Write(new PollMsg());
}};
//business logic, queue a request at any time.
this.outgoingMessageQueue.Add(new requestMessage()); 

对于设备 B:

this.comPort2.DataReceived += (a,b)=>{
    while (this.comPort2.BytesToRead > 0){
        var b = this.comPort.ReadByte();
        buffer.Add((byte)b);
    };
    if(CheckIsFullMessage(buffer))
    {
      //trigger business logic, consume the buffer and construct a response.
      //this.comPort2.Write(response,0,response.length); 
    }       
};

我注意到一件事,如果我打开设备 B,设备 A (comPort1) 的 DataReceived 将被随机延迟调用(来自 ms到秒),在延迟期间,设备A的200ms轮询从未停止过,所以我会突然从设备A获得大量数据DataReceived

谁能帮忙,为什么这两个 comPorts 会相互影响?

-----更多测试----

我做了一个测试,将 3 个 comPort 中的 3 个设备 A 连接到应用程序中,它们运行良好,没有 DataReceived 延迟。

【问题讨论】:

  • 有可能,DataReceived 是在线程池线程上触发的。因此,如果您运行的设备过多,则可能需要一段时间才能对其进行维修。 Thread.Sleep() 和 while(true) 是在 tp 线程上的彻底实践,所以首先从那里开始,使用 Thread。
  • 是的。研究调整ReceivedBytesThreshold 以潜在地限制事件频率。 Some success reported here。如果你用谷歌搜索DataReceived 问题,这似乎是很多人的共同痛点。
  • 我忘了说,“数据太多”是好事,不是坏事。保证迟早会发生。想想垃圾收集或非常繁忙的机器。所以它一定不是问题,首先解决它。您可能需要联系//fire buffer full event。它没有满,几乎没有。只获得 1 或 2 个字节是正常的。强烈建议您进行缓冲,直到您获得设备的完整响应。
  • @HansPassant 1. 你建议像这样new Thread(() =&gt; { while (true) Thread.Sleep(200);/* dequeue and send */ }).Start();,对吗?什么是我的好。 2.如果我将ThreadPool.SetMin(worker, io)设置为更高,是否有助于稳定onDataReceived
  • @NPras 设备 1 的完整消息长度从 3 到 14 不等,而设备 2 的长度为 4 到 15,并且似乎设置了 ReceivedBytesThreshold 会降低呼叫频率,对吗?我的期望是增加频率。

标签: c# .net performance


【解决方案1】:

经过一些测试和网络发布后,我在 .NET CORE 上确认了这种行为,多个SerialPort write&receive 可能会延迟DataReceived 的触发,所以我没有等待,而是添加了一个代码到 主动拉动

    public void Start()
    {
        this.comPort.DataReceived += (_,__)=>{this.PullPortDataBuffer();};
        //for very fast time accurate call(my app send&receive data every 200ms), use dedicated thread rather than timer.
        this.pullingSerialPortBufferLoop = new Thread(() =>
        {
            while (true)
            {                
                Thread.Sleep(200);
                this.PullPortDataBuffer();
            }
        });
        this.pullingSerialPortBufferLoop.Start();
    };
    var buffer = new List<byte>();
    private void PullPortDataBuffer()
    {
        if (0 == Interlocked.CompareExchange(ref this.onPullingComPortBuffer, 1, 0))
            try
            {

                while (this.comPort.BytesToRead > 0)
                {                        
                    this.buffer.Add((byte)b);
                }

                this.ConsumeBufferIfFull(this.buffer);
            }
            catch (Exception ex)
            {}
            finally
            {
                this.onPullingComPortBuffer = 0;
            }
        else
        {
            if (logger.IsDebugEnabled)
                logger.Debug(this.comPort.PortName + " concurrent enter Port_DataReceived");
        }
    }

根据我的测试,问题已经消失。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-08
    • 1970-01-01
    • 2018-12-24
    • 2011-01-18
    • 1970-01-01
    相关资源
    最近更新 更多