假设我启动一个线程在端口上接收。套接字调用将在recvfrom 上阻塞。 然后,以某种方式在另一个线程中,我关闭了套接字。
在Windows上,这将解除recvfrom的阻塞,并且我的线程执行将终止。
在 Linux 上,这不会阻止 recvfrom,因此,我的线程永远什么都不做,并且线程执行不会终止。
任何人都可以帮我解决 Linux 上发生的事情吗?当套接字关闭时,我想要recvfrom解除阻塞
我一直在阅读有关使用 select() 的内容,但我不知道如何将它用于我的具体情况。
在套接字上调用
shutdown(sock, SHUT_RDWR)
,然后等待线程退出。 (即pthread_join
)。
您可能会认为
close()
会解锁 recvfrom()
,但在 Linux 上则不然。
这是使用 select() 处理此问题的简单方法的草图:
// Note: untested code, may contain typos or bugs
static volatile bool _threadGoAway = false;
void MyThread(void *)
{
int fd = (your socket fd);
while(1)
{
struct timeval timeout = {1, 0}; // make select() return once per second
fd_set readSet;
FD_ZERO(&readSet);
FD_SET(fd, &readSet);
if (select(fd+1, &readSet, NULL, NULL, &timeout) >= 0)
{
if (_threadGoAway)
{
printf("MyThread: main thread wants me to scram, bye bye!\n");
return;
}
else if (FD_ISSET(fd, &readSet))
{
char buf[1024];
int numBytes = recvfrom(fd, buf, sizeof(buf), 0);
[...handle the received bytes here...]
}
}
else perror("select");
}
}
// To be called by the main thread at shutdown time
void MakeTheReadThreadGoAway()
{
_threadGoAway = true;
(void) pthread_join(_thread, NULL); // may block for up to one second
}
更优雅的方法是避免使用 select 的超时功能,而是创建一个套接字对(使用 socketpair()),并让主线程在需要 I/O 时在套接字对的末尾发送一个字节线程离开,并在 I/O 线程在套接字对另一端的套接字上接收到字节时退出。不过,我会将其作为练习留给读者。 :)
将套接字设置为非阻塞模式通常也是一个好主意,以避免即使在 select() 指示套接字已准备好之后,recvfrom() 调用也可能阻塞的(小但非零)机会 -请阅读,如此处所述。但阻塞模式对于您的目的来说可能“足够好”。
不是答案,但 Linux close 手册页包含有趣的引用:
关闭文件描述符可能是不明智的 由同一进程中其他线程中的系统调用使用。自从一个文件 描述符可以被重用,有一些模糊的竞争条件 可能会导致意想不到的副作用。
你正在要求不可能的事情。调用
close
的线程根本不可能知道另一个线程在 recvfrom
中被阻塞。尝试编写保证这种情况发生的代码,你会发现这是不可能的。
无论您做什么,对
close
的调用始终可以与对 recvfrom
的调用同时进行。对 close
的调用改变了套接字描述符所指的内容,因此它可以改变对 recvfrom
的调用的语义。
进入
recvfrom
的线程无法以某种方式向调用 close
的线程发出信号,表明它已被阻塞(而不是即将阻塞或刚刚进入系统调用)。因此,实际上没有可能的方法来确保 close
和 recvfrom
的行为是可预测的。
考虑以下因素:
recvfrom
,但它被系统需要执行的其他操作抢占。close
。socket
并获得与您 close
d 相同的描述符。recvfrom
,现在它正在从库打开的套接字接收数据。哎呀。
永远不要做任何像这样远程的事情。当另一个线程正在或可能正在使用某个资源时,不得释放该资源。期间。