【问题标题】:multiple threads doing poll() or select() on a single socket or pipe多个线程在单个套接字或管道上执行 poll() 或 select()
【发布时间】:2013-09-24 08:24:18
【问题描述】:

对于多个线程同时在单个套接字或管道句柄上执行poll()select() 调用的情况,POSIX 和其他标准是怎么说的?

如果有数据到达,是只唤醒一个等待线程还是唤醒所有等待线程?

【问题讨论】:

    标签: multithreading sockets posix sus


    【解决方案1】:

    有趣的问题...我通读了当前的POSIX 并没有找到具体的答案,即没有关于并发调用的规范。 所以我会解释为什么我认为标准意味着所有人都会醒来。

    textselect / pselect 的相关部分是:

    成功完成后,pselect() 或 select() 函数应修改对象 由 readfds、writefds 和 errorfds 参数指向以指示哪个文件 描述符已准备好读取、准备好写入或有错误条件未决, 分别,[...]

    以后

    当调用输入函数时,应认为描述符已准备好读取 O_NONBLOCK clear 不会阻塞,函数是否会传输数据 成功地。 (该函数可能返回数据、文件结束指示或错误 除了一个表示它被阻塞的情况,并且在每种情况下,描述符 应视为已准备好阅读。)

    简而言之(仅限阅读案例),我们可以这样理解:

    select 不会阻塞这意味着 next 调用带有 O_NONBLOCK 的输入函数不会返回带有 errno==EWOULDBLOCK 的错误。 [请注意,“下一个”是我对上述内容的解释。]

    如果承认这种解释,那么两个并发的select 调用都可能返回相同的 FD 可读。事实上,即使它们不是并发的,但是第一个线程调用 select 并且某些 FD 是可读的,后来,例如read,第二个线程在两者之间调用 select 可以返回 FD 作为第二个线程可读.

    现在问题的“唤醒”部分的相关部分是这样的:

    如果选择的描述符都没有为请求的操作做好准备,则 pselect() 或 select() 函数应阻塞,直到至少一个请求的操作变为 准备好,直到发生超时,或者直到被信号中断。

    上面的解释清楚地表明,同时等待的呼叫将全部返回。

    【讨论】:

      【解决方案2】:

      由于这个问题,我刚刚发现了一个错误: 我有两个线程在同一个套接字上进行选择,并且当 fd 作为 isset() 返回时将调用接受。事实上,两个线程的 select 都返回了,两个线程中的 fd isset(),两个线程都调用了 accept(),一个获胜,另一个阻塞等待另一个连接进入。

      所以实际上 select 会在它为同一个 fd 阻塞的所有线程中返回。

      【讨论】:

      • 这当然不足为奇。例如,线程 A 可能退出 select(),然后在它有机会调用 accept() 之前,线程 B 可能被调度运行并退出 select(),原因与描述符处于相同状态的原因相同.因此,您现在有两个线程都将调用accept()。您应该始终假设在一段代码运行和下一段代码之间会有无限长的时间,请记住在此期间可能会运行任何其他进程或线程,以及这样做是否会对您的代码造成问题.
      • 如果您知道重力是如何工作的,它就不足为奇了。但牛顿对此感到惊讶。 :-)
      【解决方案3】:

      它们都应该醒来,都返回相同的结果值,并且都对 FD 集执行相同的操作。他们都在问同样的问题,所以他们都应该得到同样的答案。

      根据此处引用的 POSIX 文档以及我仅 25 年的经验,select() 应该做的是返回可读、可写等的 FD 数量,位于那一刻。因此,所有并发的 select() 调用 not 都返回相同的东西是完全不正确的。

      select() 函数无法预测未来,即哪个线程实际上要进行读取或写入,因此哪个线程会成功。他们争辩。这是一个雷厉风行的问题。

      【讨论】:

      • 既然你无意回答这个问题,为什么不发表评论?
      • @EJP:我在 Linux 上进行测试并没有说明 HP-UX。如果可以的话,我想要一些合理的答案,而不是经验测试。
      • @sehe 在我看来,我实际上已经回答了这个问题,所以你问题的第二部分是一个不合逻辑的。
      • 好吧,这个问题问posix说什么。如果 posix 没有真正说明什么,那么在平台上观察到的任何行为都可能纯粹是偶然的 - 除非该特定平台对如何处理有自己的保证。
      • @EJP 你意识到这大约是 4 年前的事了。我不知道“你昏迷了”是什么意思。我在这里的最后一条评论是从 10:13 开始的,这意味着我只看到了 this answer。我认为人们会对只说“当然,为什么不。奇怪的问题”的答案持怀疑态度并不奇怪。不过,我喜欢当前的答案。享受投票吧。
      【解决方案4】:

      为了避免系统过载,我在内核上进行了工作,为 Linux epoll 实现了 EPOLLEXCLUSIVE。此标志设置为资源将确保只有一个侦听器将接收事件,即使在多个线程或进程正在通过 epoll()(轮询/选择的 Linux 版本)侦听给定文件描述符的情况下也是如此。这是一个非常有用的功能。例如,Enduro/X 中间件是基于多进程的中间件,其中几个负载平衡的可执行文件通过使用 epoll 监视同一组文件描述符(队列)。因此,当事件在没有 EPOLLEXCLUSIVE 的情况下到达时,许多进程会得到错误的唤醒(其中一些第一次唤醒确实已经从 FD 中删除了事件),而其他进程则得到空通知。如果有 500 个二进制文件等待事件,那么空处理会花费 CPU 处理时间......

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-10-25
        • 2014-05-09
        • 2011-10-11
        • 2016-07-27
        • 2015-07-16
        • 2013-03-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多