【问题标题】:Call recv() on the same blocking socket from two threads从两个线程在同一个阻塞套接字上调用 recv()
【发布时间】:2010-10-14 05:02:40
【问题描述】:

如果我有一个套接字,s,目前没有可用的数据,它是一个阻塞套接字,我同时从两个线程调用recv,会发生什么?其中一个线程会获取数据吗?双方都会得到吗?对recv 的第二次调用会返回错误吗?

【问题讨论】:

    标签: c sockets system recv


    【解决方案1】:

    一个线程会得到它,没有办法告诉它。

    这似乎不是一个合理的设计。为什么需要两个线程在同一个套接字上调用recv()

    【讨论】:

    • 不,我正在为一个项目自己实现这个,我想知道应该发生什么
    • 我问是因为我看不出这样设计的充分理由。线程的好处之一是隔离诸如套接字之类的东西以简化处理。相反,在线程之间共享套接字上的读取会带来很多复杂性。
    • 如果它是用于无连接协议(如 DNS)的 UDP 套接字,则从两个线程从同一个套接字读取是有意义的。然后每个线程独立处理传入的请求。
    【解决方案2】:

    套接字实现应该是线程安全的,因此只有一个线程应该在数据可用时获取数据。另一个调用应该只是阻塞。

    【讨论】:

      【解决方案3】:

      我找不到这方面的参考资料,但这是我的理解:

      供应商对线程安全的保证可能仅意味着多个线程可以各自安全地使用其自己的套接字;它不保证单个调用的原子性,也不保证套接字数据在多个线程之间的任何特定分配。

      假设线程 A 在一个套接字上调用 recv(),该套接字以高速率接收 TCP 数据流。如果 recv() 需要是原子调用,那么线程 A 可以阻止所有其他线程执行,因为它需要连续运行以提取所有数据(直到它的缓冲区已满,无论如何)。好的。因此,我不会假设 recv() 不受上下文切换的影响。

      相反,假设线程 A 在 TCP 套接字上对 recv() 进行 阻塞 调用,并且数据进入缓慢。因此,对 recv() 的调用返回时将 errno 设置为 EAGAIN。

      在任何一种情况下,假设线程 B 在同一个套接字上调用 recv(),而线程 A 仍在接收数据。线程 A 什么时候停止接收数据,以便线程 B 可以开始接收数据?我不知道有一个 Unix 实现会尝试记住线程 A 正在对套接字进行操作;相反,由应用程序(线程 A 和 B)协商使用它。

      通常,最好将应用程序设计为只有一个线程会在单个套接字上调用 recv()。

      【讨论】:

      • 我认为您在这里的假设是错误的。 recv() 获取一包数据,通常与 UDP 一起使用,使用 TCP 它仍然获取一包数据,但除非您在发送方非常小心,否则您可能会在一次接收中获取两次写入的内容。
      • 好的,我刚刚检查了文档,根据标志,它比一个数据包要复杂一些,但这是我观察到的在我从事的 Linux 应用程序中的常见情况。跨度>
      • UDP 数据报肯定比 TCP 流 wrt 线程更安全。不过,我不确定它们是 100% 线程安全的——如果你得到一个 EINTR(这还会发生吗?),那么线程 A 会过早地从 recv() 返回,给线程 B 一个跳入的机会。
      • 但是TCP没有分包;这就是为什么我们提到 TCP
      • 信号通常只中断 Unix 上的阻塞系统调用。除非您要求,否则 recv() 不会阻塞,它只是从已接收的数据包缓冲区中复制。我认为 EINTR 不会在数据包中间发生。
      【解决方案4】:

      来自recv上的man page

      SOCK_STREAM 套接字上的 recv() 返回尽可能多的可用信息 因为提供的缓冲区的大小可以 持有。

      假设您使用的是 TCP,因为问题中没有指定它。所以假设你有线程 A 和线程 B 都阻塞在 socket 的 recv() 上。一旦 s 有一些数据要接收,它将解除阻塞其中一个线程,比如说 A,并返回数据。就我们而言,返回的数据将是一些随机大小。线程 A 检查接收到的数据并确定它是否具有完整的“消息”,其中消息是应用程序级别的概念。

      线程 A 确定它没有完整的消息,因此它再次调用 recv()。但与此同时 B 已经阻塞在同一个套接字上,并且已经收到了用于线程 A 的其余“消息”。我在这里松散地使用了。

      现在线程 A 和线程 B 都有一条不完整的消息,并且会根据代码的编写方式将数据作为无效数据丢弃,或者导致奇怪和微妙的错误。

      我希望我可以说我从经验中不知道这一点。

      因此,虽然 recv() 本身在技术上是线程安全的,但如果您将它用于 TCP,那么让两个线程同时调用它是一个坏主意。

      据我所知,使用 UDP 是完全安全的。

      我希望这会有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-11-30
        • 2016-09-29
        • 2012-06-11
        • 1970-01-01
        • 2010-10-29
        • 2011-12-24
        • 2011-09-13
        相关资源
        最近更新 更多