一、背景
最近在使用 Sharp7.cs 连接西门子 PLC 时,发现当在同一台工控机上连接多个(实际超过100)CPU 时,工控机的 CPU 占用非常大,会去到 20~30%。然而此时实际的网络流量确并不高,只有10mbps。大量 CPU 资源消耗不知道在做什么。因为此时工控机上也没有跑其他业务代码。所以基本可以定位到是 Sharp7.cs 连接 PLC 这里出了问题。那么具体问题发生在哪里呢?只能看源码来发现问题了。
二、原因
在 Sharp7.cs 中发现了如下代码来读取 PLC 发送过来的数据,通过不断的读取是否有可用字节来判断一次数据是否传输完成。这样写的是可以保证读取要需要长度的数据,很多简单的 socket 编程上都可以见到。可以见到,由于代码中包含有 Sleep() 并且 sleep 的时间非常短,当同时有大量的线程都在读取数据的时候,大量的 CPU 资源消耗在了线程的切换上,造成 CPU 占用率高,效率严重不足。
1 while ((BytesReaded < Size)) 2 { 3 Thread.Sleep(2); 4 SizeAvail = TCPSocket.Available; 5 try 6 { 7 byte[] Flush = new byte[SizeAvail]; 8 int BytesRead = TCPSocket.Receive(Buffer, Start + BytesReaded, SizeAvail, SocketFlags.None); 9 BytesReaded += BytesRead; 10 } 11 catch { } 12 }
三、办法
C# 提供了 Poll() 方法来改善上述的问题。Poll() 将在指定的时间段内阻塞程序的执行直到 socket 可用。这样 socket 所在线程就会在有可用数据的时候被唤醒, 而不是不停的去测试是否有可读数据了,从而减少线程切换提高 CPU 的利用率。把代码修改如下,(注意 Poll() 方法中的时间单位是微秒)
1 while (BytesReaded < Size && TCPSocket.Poll(1000 * _ReadTimeout, SelectMode.SelectRead)) 2 { 3 int BytesRead = TCPSocket.Receive(Buffer, Start + BytesReaded, Size - BytesReaded, SocketFlags.None); 4 if (BytesRead == 0) 5 { 6 LastError = S7Consts.errTCPDataReceive; 7 Close(); 8 } 9 BytesReaded += BytesRead; 10 }
四、效果
经过上面的修改,工控机从 PLC 单次读取数据的时间从 16ms 减少到 10ms,极大的提高了 CPU 的利用率。
完。
如果你觉得这个软件对你有用还可以打赏,打赏用户将会列入打赏榜单。也接受定制服务。