试图退出阻塞的UDP套接字读取
这是一个类似于关闭阻塞UDP套接字的正确方法的问题。 我在C中有一个从UDP套接字读取的线程。 读取是阻止的。 我想知道是否有可能退出线程,而不依赖于recv()返回? 例如,我可以从另一个线程关闭套接字并安全地期望套接字读取线程退出吗? 在那个post上没有看到任何高投票的答案,这就是为什么我再次问它。
这实际上取决于您正在运行的系统。 例如,如果您在兼容POSIX的系统下运行且线程可取消,则取消线程时recv()调用将被中断,因为它是取消点。
如果你正在使用旧的套接字实现,你可以为你的线程设置SIGUSR1之类的信号处理程序,并希望没有其他人想要它并发出信号,因为recv()会中断信号。 如果可能的话,你最好的选择就是不要阻止。
我不认为关闭阻塞操作中涉及的套接字是终止操作的安全保证方式。 例如, kernel.org黑暗警告:
关闭文件描述符可能是不明智的,因为它们可能在同一进程中的其他线程中被系统调用使用。 由于文件描述符可能被重用,因此存在一些可能导致意外副作用的模糊竞争条件。
-
相反,您可以使用信号并使用
EINTR
使recv
失败(确保未启用SA_RESTART
)。 您可以使用pthread_kill
向特定线程发送信号 -
您可以在启动recv调用之前在套接字上启用
SO_RCVTIMEO
就个人而言,我通常会尽量避免所有的信号肮脏,但这是一个可行的选择。
你有几个选择。 信号将中断读操作,因此您需要做的就是确保信号熄灭。 recv操作应该失败,错误号为EINTR。
最简单的选择是设置一个计时器,在一些超时后中断你自己的进程,例如30秒:
itimerval timer timeval time; time.tv_sec = 30; time.tv_usec = 0; timer.it_value = time; if( setitimer( ITIMER_REAL, &timer, NULL ) != 0 ) printf( "failed to start timer\n" );
您将在指定时间后获得SIGALRM,这将中断您的阻止操作,并让您有机会重复操作或退出。
当另一个线程正在或可能正在使用它时,您无法释放共享资源。 在实践中,您会发现甚至无法编写代码来执行您的建议。
想一想。 当你去打电话close
,你怎么可能知道另一个线程实际上在recv
被阻止了? 如果要调用recv
,然后另一个线程调用socket
并获取刚刚关闭的描述符,该怎么办? 现在,不仅该线程没有检测到任何错误,而且它将在错误的套接字上调用recv
!
可能有一种很好的方法可以解决您的外部问题,这是您需要退出阻塞UDP套接字读取的原因。 还有一些丑陋的黑客可用。 基本方法是使套接字无阻塞,而不是阻塞UDP套接字读取,伪造带有select
或poll
的阻塞读取。 然后,您可以通过以下几种方式中止此循环:
一种方法是select
超时并在select
返回时检查’abort’标志。
另一种方法是在管道的读取端select
。 将单个字节发送到管道以中止select
。
如果是posix complient系统,你可以尝试监视你的线程: pthread_create ,其function使你的recv和pthread_cond_signal紧随其后,然后返回。
调用线程使用所需的超时生成pthread_cond_timedwait ,并在timed_out时终止被调用的线程。