多线程中使用相同的文件描述符进行`select`操作

4

如果我从多个线程调用同一个已打开的文件描述符上的 select,会发生什么?

这个问题有没有文档可以参考?


1
它可能会导致未定义的行为。不要同时在相同的文件描述符上进行select操作。 - obataku
@veer:这个在哪里有记录?(或者你是怎么知道的?) - Andrew Tomazos
在一个线程中使用select以提高可读性,在另一个线程中使用select以提高可写性是否安全? - Remy Lebeau
3
Veer错了,同时调用select是完全合法的。但是如果两个调用者都假设在单个文件描述符上执行操作不会阻塞,显然存在固有的竞态条件——只有一个线程将获得任何单个字节的数据。 - Andy Ross
1
@Andy Ross:另一个有趣的事情是:有时仍然存在竞争条件 - 例如,在侦听套接字上选择可读并不意味着客户端在调用accept时仍将尝试连接,因此它仍然可能会阻塞。 - Tony Delroy
显示剩余4条评论
2个回答

7
根据 POSIX 2008 select规范,没有禁止两个线程同时调用select的限制。
如果这两个线程都监视着有重叠文件描述符集并且一些常见文件描述符变为可读或可写或有错误被诊断,那么这两个线程可能会同时报告这些常见文件描述符已经准备好。不能保证这一点;需要考虑定时问题,可能取决于线程的调度等。这也意味着其中一个线程可能最终无法在它被告知包含要读取的数据的文件描述符上找到数据,因为另一个线程先到达了那里。每个字节的数据只会被其中一个线程读取。

5
根据Linux手册页select是一个线程安全的函数和一个取消点。
在一些操作系统上,一个线程成功进入select,而其他线程将被阻塞(select的主体部分是一个关键区域)。无论哪些描述符被返回给第一个线程,然后成功进入select的第二个线程可能会立即使用相同的集合唤醒,因为select是一个水平触发接口。
因此,在这些操作系统上不能使用select同时选择多组文件描述符。
Linux似乎支持完全可重入执行,并通过这个测试程序证明:
void * reader (void *arg) {
    int *fds = (int *)arg;
    struct timeval to = { 2, 0 };
    fd_set rfds;

    FD_ZERO(&rfds);
    FD_SET(fds[0], &rfds);

    select(fds[0]+1, &rfds, 0, 0, &to);
}

int main () {
    int sp[2];
    pthread_t t[2];
    socketpair(AF_UNIX, SOCK_STREAM, 0, sp);
    pthread_create(&t[0], 0, reader, sp);
    pthread_create(&t[1], 0, reader, sp);
    pthread_join(t[0], 0);
    pthread_join(t[1], 0);
    return 0;
}

在Linux系统(我的版本是2.6.43)上运行这个程序时,程序在2秒后返回,表明两个线程同时进入了select函数。

如果关键区域是每个线程独立的,那么第二个线程为什么会在其拥有自己的关键区域时被阻塞呢? - Andrew Tomazos
@AndrewTomazos-Fathomling:我只是指在MT意义上,没有两个线程可以同时在同一进程中进行选择。它是一个线程,然后是另一个线程。 - jxh
查看POSIX 2008 select手册页面,我没有看到任何证明两个不同线程在不同(甚至相同)的fd_sets上进行选择会相互阻塞的东西。可能在Linux实现中有一些限制,但我没有看到POSIX标准中有任何建议的东西。 - Jonathan Leffler
1
我理解的“线程安全”意味着两个线程都可以调用它而不会相互干扰。strtok() 函数是一个非线程安全函数的例子;两个线程同时使用它是不安全的。实际上,strtok() 的问题更严重;即使是单线程程序也不能同时使用两组使用 strtok() 进行分析。其他非线程安全函数包括像 ctime() 这样的珠宝;它返回指向可能在后续调用中被重用的数据区的指针。 - Jonathan Leffler
1
@JonathanLeffler:我查看了源代码,但没有找到我要找的锁。一个测试程序显示,在Linux中,select似乎是完全可重入的,所以要么自从上次我尝试后Linux在这方面发生了变化,要么我把它和另一个操作系统混淆了。 - jxh
显示剩余13条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接