POSIX和其他标准对多个线程同时对单个套接字或管道句柄进行poll()
或select()
调用的情况有何看法?
如果有任何数据到达,只有一个等待线程被唤醒或者所有等待线程被唤醒了吗?
答案 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处理时间。