C#侦听器线程上的CPU使用率高,睡眠未命中断开连接 [英] C# High CPU usage on Listener thread, sleeping misses disconnect

查看:130
本文介绍了C#侦听器线程上的CPU使用率高,睡眠未命中断开连接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面是我的连接处理程序(这是个人实验而不是生产代码)

My connection handler is below (this is more for personal experimentation than production code)

如果我不在while循环中的任何地方添加Thread.Sleep,它将开始占用CPU.相反,如果我执行Sleep来缓解无休止的while-spam,则会错过断开连接的时间..CPU上升了与正在运行的客户端/线程的数量成正比,因此,导致高使用率的不是监听器本身,而是下面发布的实际客户端线程.任何人都对如何解决这个问题有任何想法?

If I don't add a Thread.Sleep anywhere in the while loop, it starts sucking down CPU.. Conversely, if I do Sleep to alleviate the endless while-spam, I miss the disconnection.. The CPU goes up in direct proportion to the number of clients/threads running, so it's not the listener itself that's causing the high usage, it's the actual client thread posted below.. Anyone have any ideas on how to solve this?

(我避免使用基于等待的解决方案,因为我对async/await不够熟悉,并且线程化方法对于这个相当小的项目正常工作)

(I'm avoiding await-based solutions as I'm not familiar enough with async/await and the threaded method is working fine for this rather small project)

我只在SO中短暂地寻找解决方案,除了引导人们异步/等待文章之外,没有发现任何特定的问题或提供解决方案的解决方案,所以抱歉,如果我没有找到适用的答案. /p>

I only briefly searched around SO looking for a solution and didn't notice any that were this specific problem or that provided a solution other than directing folks to async/await articles, so sorry if I did miss an applicable answer.

        private void HandleConnection(CancellationToken ct) {
        int recv = 0;
        byte[] buf = new byte[4096];
        Trace.WriteLine($"{_name} Connected");
        if (_ns.CanWrite && _client.Connected) {
            _ns.Write(Encoding.BigEndianUnicode.GetBytes("■WEL"), 0, Encoding.BigEndianUnicode.GetBytes("■WEL").Length);
            try {
                while (_client.Connected && !ct.IsCancellationRequested) {

                    while (!_ns.DataAvailable) { //first attempted solution
                        Thread.Sleep(100); // miss discon if i sleep here
                        }

                    if (ct.IsCancellationRequested) {
                        Trace.WriteLine($"{(string)this} thread aborting");
                        break;
                        }

                    buf = new byte[4096];

                    if (_client.Connected && _ns.DataAvailable) {

                        recv = _ns.Read(buf, 0, buf.Length);
                        } else {
                        recv = 0;
                        }

                    if (recv > 0) {

                        string r = Encoding.BigEndianUnicode.GetString(buf);
                        r = r.TrimEnd('\0');
                        if (String.IsNullOrEmpty(r) || String.IsNullOrWhiteSpace(r))
                            r = null; //need the !not version
                        else
                            if (ParseMsg(r))
                                break;
                        }

                    //Thread.Sleep(100); // also miss discon here too

                    }
                } catch (IOException ioe) { }
            Trace.WriteLine($"{_name} Disconnected");
            if (OnDisconnected != null)
                OnDisconnected(this);
            }
        }

推荐答案

我和您有相同的问题,但是我发现解决此问题的最佳方法是:

I had the same problema than you but I found that the best way to solve this problem is:

不使用睡眠和线程阻止套接字.

升级:如果您使用线程并进入服务器,则通过每个连接接收和答复每条消息的性能都会降低.

如果您需要高性能应用程序,则不得为您接受的每个连接使用睡眠或创建线程.最好的方法是使用NetworkStream提供的Asyncronous方法,例如使用BeginReadEndRead:

If you want an High Performance App you must not use sleeps or create thread for each connection that you accept. The best way is using the Asyncronous methods that NetworkStream provides, using BeginRead and EndRead, for example:

    public void run()
    {
        server = new TcpListener(IPAddress.Any, port);
        server.Start();

        log.Info("Starting SocketServer on Port [" + port + "]");

        while (keepRunning)
        {
            try
            {
                TcpClient socket = server.AcceptTcpClient();
                if (keepRunning)
                    RequestManager.createRequestForEvalue(socket, idLayout);
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
                log.Error(ex.StackTrace);
            }
        }

        log.Info("Server Stoped.");
    }

    public static bool createRequestForEvalue(TcpClient socket, int idLayout)
    {
        Request req = null;
        req = new Request(socket,idLayout);

        registerRequest(req.ID,req); //Registra el Request, para su posterior uso.

        // DO NOT CREATE THREADS FOR ATTEND A NEW CONNECTION!!!
        //Task.Factory.StartNew(req.RunForIVR);
        //ThreadPool.QueueUserWorkItem(req.RunForIVR);

        req.startReceiveAsync(); //Recive data in asyncronus way.
        return true;
    }

    public void startReceiveAsync()
    {
        try
        {
            log.Info("[" + id + "] Starting to read the Request.");
            requestBuffer = new byte[BUFFER_SIZE];
            NetworkStream nst = socket.GetStream();
            nst.BeginRead(requestBuffer, 0,BUFFER_SIZE, this.requestReceived, nst);
        }catch(Exception ex)
        {
            log.Error("[" + id + "] There was a problem to read the Request: " + ex.Message);
            RequestManager.removeRequest(id);
            closeSocket();
        }
    }

    public void requestReceived(IAsyncResult ar)
    {

        try
        {   
        NetworkStream nst = socket.GetStream();
        int bread = nst.EndRead(ar); //Block the socket until all the buffer has been available.
        message = Encoding.UTF8.GetString(requestBuffer, 0, BUFFER_SIZE);
            log.Info("[" + id + "] Request recived: [" + message +"]");
            RunForIVR();
        }
        catch (Exception ex)
        {
            log.Error("[" + id + "] There was a problem to read the Request: " + ex.Message);
            RequestManager.removeRequest(id);
            closeSocket();
        }

    }

    public void SendResponse(String Response)
    {
        StringBuilder sb = new StringBuilder();
        sb.Append(Response);
        sb.Append('\0', BUFFER_SIZE - Response.Length);
        string message = sb.ToString();

        log.Info("[" + id + "] ivrTrans CMD: [" + idCMD + "] RESPONSE: [" + Response + "]");

        NetworkStream nst = socket.GetStream();
        byte[] buffer = new byte[BUFFER_SIZE];
        for (int i = 0; i < BUFFER_SIZE; i++)
            buffer[i] = (byte)message.ElementAt(i);

        nst.BeginWrite(buffer, 0, BUFFER_SIZE, this.closeSocket, nst);
    }

    public void closeSocket(IAsyncResult ar = null)
    {

        try
        {
            if (ar != null) //Since 4.24
            {
                NetworkStream nst = socket.GetStream();
                nst.EndWrite(ar);
            }

            socket.Close();
            socket = null;
        }catch(Exception ex)
        {
            log.Warn("[" + id + "] There was a problem to close the socket. Error: " + ex.Message + Environment.NewLine + ex.StackTrace);
        }
        log.Info("[" + id + "] Socket closed.");
    }

升级,我使用EndRead来确保所有请求均已到达.

Upgrade I use the EndRead to be sure that the request has been arrived at all.

通过其他方式,您可以使用BeginWriteEndWrite来知道套接字何时完成写操作以关闭连接

By Other way, you can use BeginWrite and EndWrite to know when the socket has been finished of write to close the connection

通过这种方式,您将持续不断地获得联系.就我而言,我将CPU使用率从30%降低到0%,每小时的请求量为15,000.

In this way you are attended the connection in a continues way and as soon as possible. In my case i reduce the CPU usage from 30% to 0%, for an a mount of 15K request per hour.

这篇关于C#侦听器线程上的CPU使用率高,睡眠未命中断开连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆