首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >高udpclient CPU使用

高udpclient CPU使用
EN

Code Review用户
提问于 2015-02-26 23:10:20
回答 2查看 2.1K关注 0票数 6

我有很多不同的端口要侦听,并迭代可用的数据发送到Dataflow管道。我总共监听了14个端口。我正在寻找关于如何减少以下代码的CPU使用的任何建议。

我只是将端口传递给一个方法,然后将它们添加到一个列表中:

代码语言:javascript
复制
   public static void AddPorts(Dictionary<int,string> ports)
{
    try
    {


            var NewEndPoints = new List<IPEndPoint>();
            foreach (var port in ports)
            {
                var endpoint = new IPEndPoint(IPAddress.Any, port.Key);
                NewEndPoints.Add(endpoint);
                if (!Endpoints.Contains(endpoint))
                {                            
                    Endpoints.Add(endpoint);
                    var client = new UdpClient(endpoint);
                    logger.Info("New Client added on port: {0}", endpoint.Port);
                    Clients.Add(client);

                }
                else
                {
                    if (IgnoredPorts.Contains(endpoint.Port))
                    {
                        logger.Info("Existing client enabled on port: {0}", endpoint.Port);
                        IgnoredPorts.Remove(port.Key);
                    }
                }
            }
            var differences = Endpoints.Except(NewEndPoints);
            differences.ToList().ForEach(d =>
            {                         
                if (!IgnoredPorts.Contains(d.Port))
                {
                    IgnoredPorts.Add(d.Port);
                    logger.Info("Client removed on port: {0}", d.Port);
                }
            });



    }
    catch (Exception ex)
    {
        logger.Error("Error creating udpclients", ex);
    } 
}

然后,我对任何可用的数据迭代套接字。

代码语言:javascript
复制
 Task.Run(async delegate
    {

        while (Receive)
        {
            try
            {
                // get any channels that have data availble
                // Iterate over the the channels and send to Dataflow pipeline
                var readyChannels =
            (from channel in Clients
             where channel.Available > 0 && !ListenersDF.IgnoredPorts.Contains(((IPEndPoint)channel.Client.LocalEndPoint).Port)
             select channel);

                // Iterate over the the channels and send to Dataflow pipeline
                foreach (var channel in readyChannels)
                {
                    // await on the result of the task
                    await ReceiveAndRespond(channel);
                }

            }
            catch (Exception ex)
            {
                logger.Error("Error sending packet to bufferBlock", ex);
            }
        }
    });

并最终将其发送到TPL数据流线。

代码语言:javascript
复制
async Task ReceiveAndRespond(UdpClient channel)
{
    UdpReceiveResult? result = null;

    try
    {
        result = await channel.ReceiveAsync();
    }
    catch (Exception exc)
    {
        logger.Error("Error receiving from channel: " + exc.Message, exc);
    }

    if (result != null)
    {
        var device = (from d in Ports
                      where d.Key == ((IPEndPoint)channel.Client.LocalEndPoint).Port
                      select d.Value).FirstOrDefault();
        UdpData data = new UdpData() { Client = channel, Data = result.Value.Buffer, LocalPort = ((IPEndPoint)channel.Client.LocalEndPoint).Port, LocalIP = ((IPEndPoint)channel.Client.LocalEndPoint).Address, RemoteEndpoint = result.Value.RemoteEndPoint, Device = device };
        Flow.bufferBlock.Post(data);

        // for testing logs the hex string to a log file              
        //logger.Warn(string.Format("Data received on port: {0} for device: {1} with data: {2}", data.LocalPort, data.Device, data.Data.ByteArrayToHexString()));              
    }
}

然后CPU坐在50%,几乎没有任何流量,我肯定有一些性能有待提高,只是不确定在哪里。

EN

回答 2

Code Review用户

回答已采纳

发布于 2015-02-28 14:11:29

请注意,Endpoints.Contains(endpoint)永远不会是真,因为您创建了一个新的IPEndPoint,并且它没有实现IEquatable接口。

除了@mjolka描述了为什么您有50%的CPU负载,我想建议您如何避免它。

目前,您有一个中央位置,可以在一个地方管理所有套接字。我建议采取不同的做法-让每个端点单独处理数据,并将所有套接字仅作为正在进行的处理(任务)的集合来管理,如下所示:

代码语言:javascript
复制
public async Task RunListenerAsync(int port)
{
    var endpoint = new IPEndPoint(IPAddress.Any, port);
    using (var client = new UdpClient(endpoint))
    {
        while (true)
        {
            try
            {
                var result = await client.ReceiveAsync().ConfigureAwait(false);
                ProcessEndpointData(client, result);
            }
            catch (Exception exc)
            {
                logger.Error("Error receiving from channel: " + exc.Message, exc);
                return;
            }
        }
    }
}

private void ProcessEndpointData(UdpClient client, UdpReceiveResult result)
{
    var localEndPoint = (IPEndPoint)client.Client.LocalEndPoint;
    var device = (from d in Ports
                    where d.Key == localEndPoint.Port
                    select d.Value).FirstOrDefault();

    var data = new UdpData
    {
        Client = client,
        Data = result.Buffer,
        LocalPort = localEndPoint.Port,
        LocalIP = localEndPoint.Address,
        RemoteEndpoint = result.RemoteEndPoint,
        Device = device
    };
    Flow.bufferBlock.Post(data);
}

注意,在这个实现中,停止侦听端口的唯一方法是在await client.ReceiveAsync()上获得一个异常。您可能需要引入CancellationToken来控制何时停止此进程。

使用这种方法,所有端口的注册和侦听将非常简单,如:

代码语言:javascript
复制
public Task ListenPortsAsync(IEnumerable<int> ports)
{
    return Task.WhenAll(ports.Select(RunListenerAsync));
}
票数 4
EN

Code Review用户

发布于 2015-02-27 03:13:05

这并不奇怪,CPU使用率是50%,它看起来你在忙-等待。

同时(接收){尝试{ var readyChannels =.(readyChannels中的var通道){.}捕获(异常ex) {.}}

来自维基百科

在软件工程中,忙碌等待或旋转是一种技术,在这种技术中,进程反复检查某个条件是否为真,例如键盘输入或锁是否可用。..。在某些情况下,旋转是一个有效的策略.然而,一般来说,旋转被认为是一种反模式,应该避免,因为可以用来执行不同任务的处理器时间被浪费在无用的活动上。

您将希望重组您的程序,以便可以删除该循环。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/82697

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档