async / await会影响tcp服务器的性能吗?
我在C#5.0中创建一个Tcp服务器,我在调用tcpListener.AcceptTcpClientAsync
和networkStream.ReadAsync
时使用了await关键字
但是当我使用Process Explorer检查我的服务器的CPU使用率时,我得到以下结果:
Tcp Sync版本: CPU使用率为10%
Tcp异步版本: 30%的CPU使用率一半的使用是内核使用。
此外,我通过在网络流的外观中添加计数器来测量我接收数据的时间,并且异步版本循环120,000次,而同步版本循环2,500,000次。
在收到的消息/秒中,当从3个不同的客户端接收消息时,异步版本比同步版本慢15%。
为什么Async Version使用比Sync版本更多的CPU?
这是因为async / await关键字吗?
这是正常的Async Tcp服务器比它的同步速度慢吗?
编辑:这是异步tcp服务器代码的示例
public class AsyncTcpListener : ITcpListener { private readonly ServerEndpoint _serverEndPoint; // Custom class to store IpAddress and Port public bool IsRunning { get; private set; } private readonly List _tcpClientConnections = new List(); private TcpListener _tcpListener; public AsyncTcpMetricListener() { _serverEndPoint = GetServerEndpoint(); } public async void Start() { IsRunning = true; RunTcpListener(); } private void MessageArrived(byte[] buffer) { // Deserialize } private void RunTcpListener(){ _tcpListener = null; try { _tcpListener = new TcpListener(_serverEndPoint.IpAddress, _serverEndPoint.Port); _tcpListener.Start(); while (true) { var tcpClient = await _tcpListener.AcceptTcpClientAsync().ConfigureAwait(false); var asyncTcpClientConnection = new AsyncTcpClientConnection(tcpClient, MessageArrived); _tcpClientConnections.Add(asyncTcpClientConnection); } } finally { if (_tcpListener != null) _tcpListener.Stop(); IsRunning = false; } } public void Stop() { IsRunning = false; _tcpListener.Stop(); _tcpClientConnections.ForEach(c => c.Close()); } }
对于每个新客户端,我们创建一个新的AsyncTcpConnection
public class AsyncTcpClientConnection { private readonly Action _messageArrived; private readonly TcpClient _tcpClient; public AsyncTcpClientConnection(TcpClient tcpClient, Action messageArrived) { _messageArrived = messageArrived; _tcpClient = tcpClient; ReceiveDataFromClientAsync(_tcpClient); } private async void ReceiveDataFromClientAsync(TcpClient tcpClient) { var readBuffer = new byte[2048]; // PacketProtocol class comes from http://blog.stephencleary.com/2009/04/sample-code-length-prefix-message.html var packetProtocol = new PacketProtocol(2048); packetProtocol.MessageArrived += _messageArrived; try { using (tcpClient) using (var networkStream = tcpClient.GetStream()) { int readSize; while ((readSize = await networkStream.ReadAsync(readBuffer, 0, readBuffer.Length).ConfigureAwait(false)) != 0) { packetProtocol.DataReceived(readBuffer, readSize); } } } catch (Exception ex) { // log } } public void Close() { _tcpClient.Close(); } }
EDIT2:同步服务器
public class TcpListener : ITcpListener { private readonly ObserverEndpoint _serverEndPoint; private readonly List _tcpClientConnections = new List(); private Thread _listeningThread; private TcpListener _tcpListener; public bool IsRunning { get; private set; } public TcpMetricListener() { _serverEndPoint = GetServerEndpoint(); } public void Start() { IsRunning = true; _listeningThread = BackgroundThread.Start(RunTcpListener); } public void Stop() { IsRunning = false; _tcpListener.Stop(); _listeningThread.Join(); _tcpClientConnections.ForEach(c => c.Close()); } private void MessageArrived(byte[] buffer) { // Deserialize } private void RunTcpListener() { _tcpListener = null; try { _tcpListener = new TcpListener(_serverEndPoint.IpAddress, _serverEndPoint.Port); _tcpListener.Start(); while (true) { var tcpClient = _tcpListener.AcceptTcpClient(); _tcpClientConnections.Add(new TcpClientConnection(tcpClient, MessageArrived)); } } finally { if (_tcpListener != null) _tcpListener.Stop(); IsRunning = false; } } }
和连接
public class TcpClientConnection { private readonly Action _messageArrived; private readonly TcpClient _tcpClient; private readonly Task _task; public TcpClientConnection(TcpClient tcpClient, Action messageArrived) { _messageArrived = messageArrived; _tcpClient = tcpClient; _task = Task.Factory.StartNew(() => ReceiveDataFromClient(_tcpClient), TaskCreationOptions.LongRunning); } private void ReceiveDataFromClient(TcpClient tcpClient) { var readBuffer = new byte[2048]; var packetProtocol = new PacketProtocol(2048); packetProtocol.MessageArrived += _messageArrived; using (tcpClient) using (var networkStream = tcpClient.GetStream()) { int readSize; while ((readSize = networkStream.Read(readBuffer, 0, readBuffer.Length)) != 0) { packetProtocol.DataReceived(readBuffer, readSize); } } } public void Close() { _tcpClient.Close(); _task.Wait(); } }
我也有async
问题,这些是我的发现: https : //stackoverflow.com/a/22222578/307976
此外,我有一个使用async
示例的异步TCP服务器/客户端,可以很好地扩展。
尝试使用ReceiveInt32Async
和ReceiveDataAsync
的以下实现来直接接收长度为前缀的消息,而不是使用tcpClient.GetStream
和networkStream.ReadAsync
:
public static class SocketsExt { static public async Task ReceiveInt32Async( this TcpClient tcpClient) { var data = new byte[sizeof(Int32)]; await tcpClient.ReceiveDataAsync(data).ConfigureAwait(false); return BitConverter.ToInt32(data, 0); } static public Task ReceiveDataAsync( this TcpClient tcpClient, byte[] buffer) { return Task.Factory.FromAsync( (asyncCallback, state) => tcpClient.Client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, asyncCallback, state), (asyncResult) => tcpClient.Client.EndReceive(asyncResult), null); } }
看看这是否有任何改进。 另外,我还建议将ReceiveDataFromClientAsync
作为async Task
方法并存储它在AsyncTcpClientConnection
内返回的Task
(用于状态和错误跟踪)。