我有一个应用程序可以同时生成几百个TCP连接,并从它们接收一个恒定的数据流。
private void startReceive()
{
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
e.Completed += receiveCompleted;
e.SetBuffer(new byte[1024], 0, 1024);
if (!Socket.ReceiveAsync(e)) { receiveCompleted(this, e); }
}
void receiveCompleted(object sender, SocketAsyncEventArgs e)
{
ProcessData(e);
if (!Socket.ReceiveAsync(e)) { receiveCompleted(this, e); }
}
我的尝试导致了这样的事情:
private async void StartReceive()
{
byte[] Buff = new byte[1024];
int recv = 0;
while (Socket.Connected)
{
recv = await NetworkStream.ReadAsync(Buff, 0, 1024);
ProcessData(Buff,recv);
}
}
我遇到的问题是调用StartReceive()
的方法会阻塞,而不是到达随附的StartSend() method called after
StartReceive(). Creating a new task for
StartReceive()would just end up with 300-ish threads, and it seems to do so just by calling
StartReceive()`反正。
在使用async
时,在现有代码上实施新await
和NetworkStream
关键字的正确方法是什么?所以它使用Socket.SendAsync()
的线程池和Socket.ReceiveAsync()
正在使用以避免必须拥有数百个线程/任务?
以这种方式使用networkstream
与使用beginreceive
的i / o完成端口相比,是否有任何性能优势?
答案 0 :(得分:25)
您在这里一次更改两件事:异步样式(SocketAsyncEventArgs
到Task
/ async
)和抽象级别(Socket
到{{1 }})。
由于您已熟悉NetworkStream
,我建议您只更改异步样式,然后直接继续使用Socket
类。
Async CTP没有给Socket
任何Socket
兼容的方法(这很奇怪;我认为它们被错误地遗漏了,并将在.NET 4.5中添加)。
如果您使用我的AsyncEx library,那么创建自己的async
扩展方法(以及其他操作的类似包装器)并不难:
ReceiveAsyncTask
完成此操作后,您的public static Task<int> ReceiveAsyncTask(this Socket socket,
byte[] buffer, int offset, int size)
{
return AsyncFactory<int>.FromApm(socket.BeginReceive, socket.EndReceive,
buffer, offset, size, SocketFlags.None);
}
可以这样写:
StartReceive
现在,要解决许多小问题:
private async Task StartReceive()
{
try
{
var buffer = new byte[1024];
while (true)
{
var bytesReceived = await socket.ReceiveAsyncTask(buffer, 0, 1024)
.ConfigureAwait(false);
ProcessData(buffer, bytesReceived);
}
}
catch (Exception ex)
{
// Handle errors here
}
}
不会产生新线程。我和其他许多人一样写了an async/await intro on my blog。 await
/ async
允许并发,但这并不一定意味着多线程。await
/ async
不是一种全新的异步处理形式;它只是一种更简单的方式来表达异步处理。它仍然使用下面的IOCP。 await
/ async
的效果略低于较低级别的方法;它的吸引力在于编写和编写异步方法的易用性。await
/ async
时可能会看到GC压力增加。并联团队wrote up some example socket-specific awaitables上的Stephen Toub可以帮助解决这个问题。 (我建议首先使用简单的模式,如果你觉得有必要,只使用性能增强的方法;但是,如果最终需要它,最好知道它在那里。)await
,除非你真的需要它们才能返回Task
。 void
是等待的,因此您的方法是可组合的(并且更容易测试); Task
更像是“火与忘记”。void
告诉其余的ConfigureAwait(false)
方法在线程池线程上执行。我在上面的示例中使用了这个,以便在线程池线程中执行async
,就像使用ProcessData
时一样。SocketAsyncEventArgs
没用。 You need to send data to detect if the connection is still valid.