异步写入套接字线程是不是安全?

Posted

技术标签:

【中文标题】异步写入套接字线程是不是安全?【英文标题】:Are Asynchronous writes to a socket thread safe?异步写入套接字线程是否安全? 【发布时间】:2012-04-26 03:46:47 【问题描述】:

考虑Socket.BeginSend() 方法。如果两个线程池线程同时调用此方法,它们各自的消息最终会相互混合还是套接字类阻止了这种情况发生?

【问题讨论】:

虽然它可以工作,但您应该只使用不可靠的基于排序数据报的协议,如 UDP。原因是如果两个线程竞相调用 BeginSend,您可能会在源头发送乱序。这将导致使用 TCP 时应用程序数据损坏。 【参考方案1】:

我找到了一个类似的post on the MSDN forum,它似乎可以回答你的问题。

    您可以同时对多个 BeginSend 进行排队。你不需要锁定

编辑:

更多有趣的信息:

如果您在Remark section of the MSDN doc BeginSend() 中向下滚动一点,您会发现可能与您相关的回调方法的有趣用法。

[...] 如果您希望原始线程在调用 BeginSend 方法后阻塞,请使用 WaitHandle.WaitOne 方法。 [...]

【讨论】:

这正是我想要的!谢谢!【参考方案2】:

.NET Socket 实例不是线程安全的,因为同时调用某些方法(相同或不同)可能会导致状态不一致。但是,BeginSend()BeginReceive() 方法对于它们自身来说是线程安全的

对每个(或两个)都有多个未完成的调用是安全的。

BeginReceive() 的情况下,当数据按调用顺序可用时,它们将得到服务。例如,如果您的处理时间很长,但您希望其他接收尽快发生,这可能很有用。当然,在这种情况下,您的代码可能会同时处理多个收据,并且您可能需要自己的同步逻辑来保护您的应用程序状态。

BeginSend() 的情况下,每次调用都会尝试将发送的数据推送到套接字缓冲区,一旦在那里被接受,您的回调将被调用(您将调用EndSend())。如果任何调用没有足够的缓冲空间,它将阻塞。

注意:不要假设默认的 8k 缓冲区意味着“我可以用正好 8k 的数据快速调用BeginSend(),然后它会阻塞”,因为以下是正确的:

    8K 是一个“标称大小”的数字,缓冲区可能会有所缩小和增长

    当您排队 8K 的呼叫时,将在网络上发送数据,从而减少 8K 的排队数据

一般:

如果您在一个线程中多次调用BeginSend(),您可以确保发送将按照调用顺序离开机器。

如果您从多个线程调用BeginSend(),则无法保证顺序,除非您使用其他阻塞机制来强制实际调用以某种特定顺序发生。但是,每个调用都会在一个连续的网络字节流中正确发送其数据。

【讨论】:

您的回答是在最初的问题之后几年才出现的,我现在才看到它(又过了一年),但我想让你知道它有很多细节,我真的很感激。虽然,我个人对 .Net 框架的了解和我作为开发人员的技能现在远远超出了这个问题,但这个答案对于未来的读者来说将是一个很好的资源。如果几年前我还没有将答案作为正确答案进行检查,我会给你正确的答案。非常感谢。

以上是关于异步写入套接字线程是不是安全?的主要内容,如果未能解决你的问题,请参考以下文章

C++ Boost.Asio - tcp 套接字异步写入

boost::asio::socket 线程安全

是否可以同时读取和写入 java.net.Socket?

Java套接字多线程安全吗?

在 C 或 C++ 中的同一个套接字上同时读取和写入

ZMQ 套接字不是线程安全的,但我可以在不同的线程中使用 zmq_send() 和 zmq_recv() 吗?