多个线程在单个套接字或管道上执行poll()或select()

时间:2013-09-19 09:51:44

标签: multithreading sockets posix sus

POSIX和其他标准对多个线程同时对单个套接字或管道句柄进行poll()select()调用的情况有何看法?

如果有任何数据到达,只有一个等待线程被唤醒或者所有等待线程被唤醒了吗?

4 个答案:

答案 0 :(得分:17)

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

select / pselect text的相关部分是:

  

成功完成后,pselect()或select()函数将修改对象   readfds,writefds和errorfds参数指向哪个文件指示   描述符已准备好进行读取,可以写入,或者有错误条件待处理,   [...]

以后

  

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

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

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

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

现在问题“醒来”部分的相关部分是:

  

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

这里显然上述解释表明同时等待的呼叫都会返回。

答案 1 :(得分:4)

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

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

答案 2 :(得分:2)

他们都应该醒来,都返回相同的结果值,并且所有人都对FD集做同样的事情。他们都在问同样的问题,所以他们都应该得到相同的答案。

根据此处引用的POSIX文档,以及我仅仅25年的经验,select()应该做什么,就是返回可读,可写等的FD数量。那一刻。因此,对于所有并发的select()调用而不是而言,所有返回相同的内容都是完全错误的。

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

答案 3 :(得分:1)

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