是否可以在 .NET 的异步回调中进行繁重的处理,在返回之前将它们占用几秒钟?还是我剥夺了操作系统/重要资源的运行时?
例如,考虑TcpListener.BeginAcceptSocket
. 我的回调从调用 开始EndAcceptSocket
,然后花一段时间接收数据,然后才关闭套接字并返回。这是它的使用方式,还是我希望在我自己的线程上进行额外的处理?
是否可以在 .NET 的异步回调中进行繁重的处理,在返回之前将它们占用几秒钟?还是我剥夺了操作系统/重要资源的运行时?
例如,考虑TcpListener.BeginAcceptSocket
. 我的回调从调用 开始EndAcceptSocket
,然后花一段时间接收数据,然后才关闭套接字并返回。这是它的使用方式,还是我希望在我自己的线程上进行额外的处理?
是的,这就是异步套接字(Tcp 客户端、侦听器等)的使用方式。您应该始终确保调用 end aysnc 方法,然后执行您想要的任何处理。不调用 EndAccept()、EndSEnd()、EndReceive() 等方法可能会导致内存泄漏,因此遵循此方法始终是一个好习惯。
使用的线程与您自己手动设置后台线程没有什么不同,并且实际上设计用于几秒钟的“长期操作”。相信我,您不希望在调度或 GUI 线程上运行任何需要很长时间的东西。
我有超过 90 个基于移动的系统,它们使用异步套接字与服务器通信,它做得非常好:比 Web 服务快得多(记住所有 Web 协议都在 Socket 之上运行),易于检测错误等,等等
我在我的服务器代码上做同样的事情(与其他一些用于中间件和后端通信的 WCF 混合在一起),它是我们使用过的最可扩展的通信。您必须在 Google 上对其进行搜索,但是有一个人使用该技术发布了他的测试,他能够支持 1,000 个并发通信到只有 11 个线程的服务器。不错。
来自 MS 的服务器示例:http: //msdn.microsoft.com/en-us/library/fx6588te.aspx
来自 MS 的客户端示例:http: //msdn.microsoft.com/en-us/library/bew39x2a.aspx
让它“完美”需要的不仅仅是这些,但这是一个很好的起点。
我重复了一篇关于该主题的 CodeProject 文章的实验,发现 .NET 4 的结果与 2003 年描述的结果相似。请注意,这篇文章实际上并没有列出有问题部分的结果,但据我了解,主要是问题依然存在。
我重用了 CodeProject 文章中的代码 - 只需下载它即可自己运行此测试或进行实验。
该测试将尝试使用 10 个并行线程在 1 秒内尽可能多地计数。
使用 10 个后台线程(即new Thread()
)
T0 = 4451756 T1 = 4215159 T2 = 5449189 T3 = 6244135 T4 = 3297895 T5 = 5302370 T6 = 5256763 T7 = 3779166 T8 = 6309599 T9 = 6236041 总计 = 50542073
使用 10 个线程池工作项
T0 = 23335890 T1 = 20998989 T2 = 22920781 T3 = 9802624 T4 = 0 T5 = 0 T6 = 0 T7 = 0 T8 = 0 T9 = 0 总计 = 77058284
请注意,在 1 秒的时间片中实际执行的 10 个线程池工作项中只有 4 个!这是在四核 CPU 上,因此每个内核一个线程。在前四个完成后执行的其他任务,因为分配的 1 秒已经过期,所以它们没有增加计数器。
这里的结论是:对于长任务,ThreadPool 会使一些任务落后于其他任务!因此,我强烈建议不要在 ThreadPool 任务中进行任何长时间的处理(例如异步完成处理程序)。否则,如果您的数据处理占用了 CPU,您可能会阻止更重要的异步调用完成,或者如果只有某些任务执行大量处理,您可能会有非常不稳定的性能。
使用文章中的自定义 ThreadPool 实现
T0 = 7175934 T1 = 6983639 T2 = 5306292 T3 = 5078502 T4 = 3279956 T5 = 8116320 T6 = 3262403 T7 = 7678457 T8 = 8946761 T9 = 8500619 总计 = 64328883