flysun027

有思想的博客

导航

在 C# 中使用 Socket.Poll() 提高 CPU 利用率

Posted on 2024-08-13 10:28  flysun027  阅读(165)  评论(0编辑  收藏  举报

一、背景

        最近在使用 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 的利用率。

    完。

如果你觉得这个软件对你有用还可以打赏,打赏用户将会列入打赏榜单。也接受定制服务。