【问题标题】:Is serializing a socket through Lock really necessary when only sending and receiving?仅在发送和接收时,是否真的需要通过 Lock 序列化套接字?
【发布时间】:2018-12-10 07:30:52
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: python-3.x multithreading sockets locking


    【解决方案1】:

    没有理由必须将调用同步到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 字节的消息作为一个单元。但这并不能保证,当服务器/网络变得繁忙并且操作系统网络缓冲区填满并且您有多个线程争夺 CPU 时,这种情况更有可能发生。 (顺便说一句,它不仅仅是 python;C 程序也是如此。)

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

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

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

    【讨论】:

    • 感谢您的精彩解释。现在我明白了。而且由于我所有的线程都只从套接字读取,我将不得不同步它们。
    猜你喜欢
    • 2012-02-12
    • 2013-09-11
    • 1970-01-01
    • 2010-11-29
    • 1970-01-01
    • 2011-06-21
    • 1970-01-01
    • 2016-12-08
    • 2012-10-05
    相关资源
    最近更新 更多