C#监听器线程的CPU使用率很高,睡眠未命中断开

我的连接处理程序如下(这比个人实验更多,而不是生产代码)

如果我没有在while循环中的任何地方添加一个Thread.Sleep,它会开始吸收CPU。相反,如果我做睡眠以缓解无休止的垃圾邮件,我会错过断开连接.. CPU的比例正常上升对于运行的客户端/线程的数量,所以不是监听器本身导致高使用率,它是下面发布的实际客户端线程..任何人对如何解决这个问题都有任何想法?

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

我只是在SO周围寻找解决方案,并没有注意到任何这个特定的问题,或者提供了除了指导人们异步/等待文章之外的解决方案,所以很抱歉,如果我错过了适用的答案。

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); } } 

我遇到的问题与你相同,但我发现解决这个问题的最佳方法是:

没有用睡眠和线程阻塞套接字。

升级如果您使用线程并进入服务器,则每个连接接收和回复每条消息的性能都会很低。

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

  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来确保请求已经到达。

通过其他方式,您可以使用BeginWriteEndWrite来了解套接字何时完成写入以关闭连接

通过这种方式,您将以尽可能快的方式参加连接。 在我的情况下,我将CPU使用率从30%降低到0%,每小时一次15K的请求。

通过套接字进行通信的正确方法是:

  1. 不断阅读。 这些读操作将阻塞,直到数据进入或直到套接字正常断开(读取完成时读取0字节)。
  2. 定期写。 需要这些写入以确保连接仍然可行 。

正确的线程方法每个连接需要两个线程。 我不相信它比异步方法更简单。

PS如果您的代码使用Connected ,则它有一个错误。 正确的解决方案永远不需要使用Connected