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

时间:2018-12-10 07:30:53

标签: python-3.x multithreading sockets locking

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

在寻找答案时,大多数人只是简单地说套接字不是线程安全的,因此需要使用某种方法来序列化它们。但是没有人真正解释为什么要这样做。

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

如果不正确地调用sendrecv而没有锁,那为什么会这样呢?线程上是否有可能接收到另一个请求的数据?

1 个答案:

答案 0 :(得分:2)

只要只有一个发送者或接收者,就没有理由必须同步对sendrecv 的呼叫。考虑是否有两个线程试图接收消息。假设出于讨论的目的,client => server消息长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 BTW; C程序也是如此。)

因此,是的,套接字是“线程安全的”,因为恰好一个线程的recv将获得给定的数据字节,并且没有什么可以阻止两个线程同时调用recv的。操作系统不会对此感到困惑。但是,TCP(SOCK_STREAM)套接字中没有任何东西可以确保一个线程接收到整个“消息”(长度大于单个字节)。

如果您有两个线程,一个线程接收一个 ,一个线程发送一个 ,那么就不会有竞争,也不需要锁定就可以保持一致的数据流。

UDP套接字OTOH是“面向消息的”,因此它们没有相同的问题。每个sendrecv传递一个不可分割的数据报。接收线程不会获得数据报的 part 。它得到的是整个数据报或什么都不接收(尽管由于缓冲空间不足,数据报可以被截断;但是在那种情况下,剩余的数据将被简单地丢弃而不发送给下一个接收线程)。