调用SocketAsyncEventArgs完成的线程以及处理消息的位置

时间:2017-07-16 20:33:55

标签: c# multithreading sockets asynchronous

我有一个使用SocketAsyncEventArgs异步接收UDP数据包的标准实现。从文档和谷歌搜索中我不理解的是,我是否应该在回调本身内处理消息的实际工作,例如我所指的完整实现中的this comment indicates,或者我应该将处理卸载到其他线程,例如通过ConcurrentQueue或BlockingCollection。

我的担忧如下:

  • 如果直接在回调中处理,是否会因为临时线程池饥饿或其他一些实现细节而降低接收性能或引入随机延迟?
  • 由于处理的轻微延迟(与仅卸载到集合)可能因为完整的缓冲区而丢弃更多的包?
  • 可以重新排序更多的包,因为回调的调用顺序与包实际上从网络到达的顺序不同。

那么使用SocketAsyncEventArgs处理消息的最佳实践或预期方法是什么,以确保最小的丢失数据报,没有额外的回调调用重新排序和没有额外的延迟?

一个相关的问题 - ReceiveAsync是否保证任何订单或至少尝试按照从网络收到包的相同顺序调用回调,或者我应该使用阻止接收?目标用例是订阅6-8个UDP通道,在每个通道中,顺序非常重要。运行许多阻塞线程看起来比处理回调更复杂,但如果只有这样的解决方案保证了消息顺序,那就不那么难了。

1 个答案:

答案 0 :(得分:3)

  

使用SocketAsyncEventArgs处理消息以确保最小丢失数据报的最佳实践或预期方法是什么

坦率地说,这在某种程度上确实是一种意见问题,并且非常依赖于您的确切情况。但是,作为一般规则,您的I / O完成例程应该很快。如果您的处理速度很快,那么在例行程序中执行此操作就可以了。如果没有,你应该做尽可能少的工作,即只是将数据移动到可以在其他地方处理的队列,然后从完成例程返回。

请记住,这里“快”是相对的。您需要比网络更快,尽管网络速度不断提高,但在现代CPU上并不是很难。网络层将代表您缓冲,因此假设您的工作负载吞吐量大于网络上的吞吐量,您可能在完成例程中完成工作。

但实际上,这只是取决于。没有办法说一般哪个更好。每种具体情况都不同。

  

ReceiveAsync是否保证任何订单,或者至少尝试按照从网络收到包的顺序调用回调,或者我应该使用阻止接收?

阻止接收无济于事。

异步方法都具有相同的特征:您可以一次发出多个,并且它们将按照发布它们的顺序完成。但您仍需要跟踪发出读取操作的顺序。缓冲区将按照您将它们提供给网络层的顺序填充,但完成例程可能无序执行,因为它们在线程上执行,并且线程调度程序不保证线程执行的顺序。仅仅因为一个线程在另一个线程之前可以运行,这并不意味着它实际上会在另一个线程之前获得它的下一个时间片。

但它实际上比那更糟:

  

目标用例是订阅6-8个UDP通道,每个通道中的顺序非常重要。

如果订单在您的方案中很重要,您需要在数据报中包含序列号,并确保在收到这些数据报时使用这些序列号以正确的顺序放置数据。

UDP不保证订购。可以按任何顺序接收数据报,而不管它们的发送顺序如何。 UDP也不保证交付。可以随时删除数据报。 UDP也不保证唯一性。给定的数据报可以多次传送。

如果在您的方案中可靠性和排序很重要,您可能应该使用TCP,而不是UDP。

如果您唯一关心的是订购,那么UDP可能仍然适合您。在这种情况下,由于您无论如何都需要数据报中的序列号,它确实使“多个并发读取操作”场景变得更简单,因为数据本身带有序列号,因此您不需要单独跟踪它(例如,在与每个读操作相关联的状态对象中。)