按顺序运行异步套接字操作
Posted
技术标签:
【中文标题】按顺序运行异步套接字操作【英文标题】:Running async socket operations in order 【发布时间】:2012-07-17 09:24:37 【问题描述】:我需要的是一个按顺序执行异步操作的类。
class FooSocket
private Socket _Socket;
// Message is a class that wraps a byte array.
public Task<Message> Receive() /*Bla...*/ ;
public Task<int> Send(Message message) /*Bla...*/ ;
如果我按Send、Receive和Send的顺序调用,我需要先发送,并将剩余的接收和发送操作排队,直到第一个接收完成。
我尝试在课堂上创建一个主要任务字段并遵循MainTask = MainTask.ContinueWith(...)
类型的方法。我什至写了一个名为 Sequencer 的类,它正是这样做的,但不知何故感觉不对,在延续和其他东西中创建嵌套任务(使用 Task.Factory.FromAsync 方法)。
我还尝试做一些事情,比如排队 TaskCompletionSource 对象并在我的 Receive/Send 方法中返回它们的任务,在一个单独的线程上以无限循环检查队列,但由于我将有大约 200k 的 FooSocket 实例,一个线程用于各自也觉得不明智。如果我把它做成一个线程池,我会得出这个“线程池不应该用于长时间运行的操作”的规则。
我感觉很接近,但不确定订购这些工作的最有效方式是什么。
【问题讨论】:
如果您可以使用 C#5 或异步扩展,甚至更好的 F#,如果不是,那么您应该使用 Task.FromAsync/Task.ContinueWith 方式,恕我直言 您想同步运行异步操作吗? @Carsten,我需要在 C# 4 中完成。 @Jodrell,有点;当涉及到它们自己的操作时,所有 FooSocket 实例都应该同步工作,而从调用者的角度来看是异步的。 @d4wn:或多或少是微不足道的——你基本上只需要添加一些 async/await 关键字——你可以在这里找到很多示例和信息:msdn.microsoft.com/en-us/vstudio/async.aspx 【参考方案1】:我会使用TPL Dataflow。您可以安装它via NuGet 或作为Async CTP 的一部分。 TPL Dataflow 提供了一个基本的 BufferBlock<T>
类型,听起来正是您所需要的。
如果你只是建模一个套接字,那么当数据被缓冲出去时完成Send
任务,不断地读入另一个缓冲区,当你从那个缓冲区读取时完成Receive
任务。 (注意:Socket
上的“发送”操作在数据缓冲到操作系统时完成,而不是在数据通过线路输出或到达目的地时完成。
如果您正在为更高级别的命令/响应建模,那么您应该有一个代表整个命令/响应的任务,正如 James 所建议的那样。我会两者兼而有之;借助async
/await
支持,可以轻松将一个层叠在另一个之上。 (注意:Socket
上的“接收”操作可能会以部分接收完成,因此您需要 message framing)。
您的架构可能还需要一些工作:
我将拥有大约 20 万个 FooSocket 实例
这肯定是个问题(我假设您使用的是 TCP/IP)。只有大约 65K TCP/IP 端口,默认情况下只有大约 16K 是短暂的,您必须为操作系统留出大量“喘息空间”,否则它会开始出现异常行为。我估计只有约 12K 的连接是现实的,除非您更改可以(理论上)使您达到约 59K 的短暂范围。大约 200K 是不可能的 - 除非您更改临时范围并且使用负载均衡器拥有多个 IP 地址。
【讨论】:
关于 BufferBlock如果它是请求/响应类型的行为,恕我直言,任务粒度应该是完整的往返,而不是分开发送和接收。
您不需要 CTP 来支持 .net 4,您可以使用异步定位包。
消费代码是什么样的?带有等待调用的循环?
【讨论】:
以上是关于按顺序运行异步套接字操作的主要内容,如果未能解决你的问题,请参考以下文章