如何让一个阻塞在recv()上的线程优雅地退出?

时间:2013-08-16 06:32:19

标签: c multithreading sockets unix

有一个线程喜欢这个:

{  
    ......    
    while (1)
    {
        recv(socket, buffer, sizeof(buffer), 0);
        ......
    }
    close(socket);           
}  

因为线程在recv()调用时阻塞,我怎么能让线程正常退出?

7 个答案:

答案 0 :(得分:5)

您可以致电: -

shutdown(sock, SHUT_RDWR)    //on the remote end

检查this

答案 1 :(得分:5)

不要阻止recv。而是在'select'上实现一个块并测试输入fdset以确定是否有任何东西要读取。

如果有要读取的数据,请调用recv。如果没有,循环,检查'isrunning'volitile布尔变量,当启动关闭时由其他线程设置。

答案 2 :(得分:3)

或者:

  1. 使用setsockopt()SO_RCVTIMEO设置读取超时,并在触发时检查状态变量,看看您是否已经告诉自己停止阅读。
  2. 如果您想要永久停止阅读套接字,请将其关闭,以便使用shutdown(sd, SHUT_RD)进行输入。这将导致recv()从现在开始返回零。
  3. 将套接字设置为非阻塞模式,并使用带有超时的select()告诉您何时读取,采用与上述(1)中的状态变量相同的策略。然而,非阻塞模式在发送操作中引入了相当多的复杂性,因此您应该更喜欢上面的(1)或(2)。
  4. 您的伪代码缺少EOS和错误检查。我希望它看起来不像那样。

答案 3 :(得分:2)

声明全局退出标志:

int bExit = 0;

让cirtical部分测试它:

 while(1)
 {
   ssize_t result = recv(socket, buffer, sizeof(buffer), 0);
   if ((-1 == result) && (EINTR == error) && bExit) 
   {
     break;
   }

   ...
 }

要破坏读者,请先设置退出标记

bExit = 1;

然后向读者线程发送信号

pthread_kill(pthreadReader, SIGUSR1);

注意: 我在这个例子中遗漏的是bExit对并发访问的保护。

这可以通过使用互斥或​​适当的声明来实现。

答案 4 :(得分:2)

声明一些'stop'布尔值,在每次recv()返回后检查它,如果设置则终止。要关闭,请设置bool并从另一个线程关闭套接字。阻塞recv()将立即返回“错误”,但无论如何“无论如何都要终止:”

答案 5 :(得分:1)

我可能会使用@ alk的罚款answer中的信号(也讨论过here)。

或者,您可以使用多路复用I / O.

在初始化时,创建一个全局管道(2)。当是时候结束程序时,关闭管道的写入端 - 现在读取端将立即选择(2)/ poll(2)可读(对于EOF)。同时,让你的recv-blocked线程包括这个管道的读端以及它们的套接字,例如,无限期阻塞select(2)调用。如果管道的读取端返回可读,则I / O线程知道是时候正常终止。

这种技术的主要警告是确保写入端只关闭一次,因为后续的,天真的关闭(2)可能会扼杀一些无辜的文件,这些文件恰好被赋予与管道旧写入相同的描述符末端。

答案 6 :(得分:0)

另外,将套接字设置为非阻塞:

int listen_sd = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (listen_sd < 0) {
    perror("socket() failed");
}

int on = 1;

//set socket to be non-blocking
int rc = ioctl(listen_sd, FIONBIO,(char *)&on);
if (rc < 0) {
    perror("ioctl() failed");
    close(listen_sd);
}

然后,你可以在套接字上调用recv(),它不会阻塞。如果没有什么可读的,则ERRNO全局变量设置为常量EWOULDBLOCK

int rc = recv(listen_sd, buffer, sizeof(buffer), 0);
if (rc < 0) {
    if (errno != EWOULDBLOCK) {
        perror("recv() failed");
    }
}