我调用 FD_SET() 来设置非阻塞套接字的 write_fd,而 select() 在另一个线程中阻塞 - 问题在于,即使 fd 已准备好写入,select() 也会保持阻塞。
我真正想做的是:在另一个线程中准备要为此套接字写入的数据,然后将套接字添加到 write_fd 中。 select() 线程应该识别这一点并处理准备好的数据。
select() 在阻塞时不能识别 fd 中的变化吗? 如果是 - 是否有类似 epoll() EPOLL_CTL_MOD 而不是 FD_SET() 的东西来更新集合;或者是识别更改以设置 select() 函数超时的唯一方法?
在我看来,这不是一个解决方案,因为那会“慢”并且会产生 CPU 开销......
编辑:
// This thread is running all day long ...
static void * workman() {
FD_ZERO(&fd_read);
FD_ZERO(&fd_write);
FD_SET(socketId , &fd_read);
while(1) {
// PROBLEM Keeps blocking when box() is called
select(socketId+1, &fd_read, &fd_write, NULL, NULL);
if(FD_ISSET(socketId, &fd_read)) {
// RECIVE DATA
}
else if(FD_ISSET(socketId, &fd_write)) {
FD_CLR(socketId, &fd_write);
pthread_mutex_lock(&interface.mutex);
strncpy(conn.outBuffer, interface.buffer, strlen(interface.buffer));
interface.buffer[0] = '\0';
pthread_mutex_unlock(&interface.mutex);
// SEND DATA
}
}
return 0;
}
// This function is called within another thread on user input
int box(char *content) {
pthread_mutex_lock(&interface.mutex);
// preparing the data and write it into interface.buffer if available
pthread_mutex_unlock(&interface.mutex);
FD_SET(socketId, &fd_write);
return 0;
}
是的,正如您所怀疑的,
select()
不会检测到另一个线程对文件描述符集所做的更改。毕竟,如果没有某种神奇的机制来异步检测对特定内存位置的写入,它就无法有效地做到这一点。
并且,是的,您应该使用
epoll
界面。 epoll_wait
的手册页特别指出处理来自另一个线程的更改。
当一个线程在调用 epoll_pwait() 时被阻塞,它是 另一个线程可以向其中添加文件描述符 等待 epoll 实例。如果新的文件描述符准备就绪, 它会导致 epoll_wait() 调用解除阻塞。
但是,如果您无法使用
epoll
或其他支持此类更改的文件通知界面,仍然有解决方案。您可以使用内部 pipe
(或类似的机制,例如 eventfd
),使 select
在更新文件描述符集后返回并重新启动。当然,您应该小心地进行适当的锁定以避免竞争条件。同样,请确保将管道置于非阻塞模式,否则管道写入端的 write
可能会在重负载下阻塞,并可能导致程序死锁。
为了执行类似的操作,select 需要使用“当前”fd 集进行内部轮询,这实际上是 CPU 开销,并且您将无法控制轮询的周期。我认为这就是 select 的用途。