仅发送和接收时真的需要通过 Lock 序列化套接字吗?

Is serializing a socket through Lock really necessary when only sending and receiving?

我想知道在使用 Pythons 套接字时是否真的需要使用例如 threading.Lock,其中每个线程仅从该套接字发送和接收。打开和关闭始终由父级处理。

在搜索答案时,大多数人只是说套接字不是线程安全的,需要使用某种东西来序列化它们。但是没有人真正解释为什么需要这样做。

其他人说 sendrecv 在操作系统级别上是线程安全的,因此可以并行使用而无需序列化 (here)。我不知道我在这里是否正确,但我认为 Python 使用 POSIX 套接字的 C 实现,对吗? (套接字的 Windows 实现怎么样?)

如果 sendrecv 在没有锁的情况下被调用是不正确的,那究竟是为什么?线程上是否有可能接收到另一个请求的数据?

只要只有一个发送方或接收方,就没​​有理由必须同步对 sendrecv 的调用。考虑是否有两个线程试图接收一条消息。假设,为了便于讨论,客户端 => 服务器消息的长度为 8 个字节,前 4 个字节是命令,后 4 个字节是某种对象标识符:

Thread A: sock.recv(8) obtains first 4 bytes
Thread B: sock.recv(8) obtains second 4 bytes

在这里,两个线程都没有以完整的消息结束。现在,很多时候 不会 发生,你会得到整个 8 字节消息作为一个单元。但这并不能保证,当服务器/网络繁忙并且 OS 网络缓冲区填满并且您有多个线程争夺 CPU 时,这种情况更有可能发生。 (这不仅仅是 python 顺便说一句;C 程序也是如此。)

所以,是的,套接字是 "thread-safe",因为只有一个线程的 recv 将获得给定的数据字节,并且没有什么可以阻止两个线程同时调用 recv时间。 OS 不会因此而感到困惑。但是 TCP (SOCK_STREAM) 套接字中没有任何东西可以确保一个线程接收到整个 "message"(任何长度大于一个字节)。

如果你有两个线程,一个接收,一个发送,那么就没有竞争,也没有锁定需要保持连贯的数据流。

UDP 套接字 OTOH 是 "message-oriented" 所以他们没有同样的问题。每个 sendrecv 传递一个不可分割的数据报。接收线程不会获得数据报的 part。它得到整个数据报或什么都没有(尽管数据报可能由于缓冲区不足 space 而被截断;但在那种情况下,其余部分只是被丢弃 而不是 传递给下一个接收线程).