9

假设我启动一个线程以在端口上接收。套接字调用将在 recvfrom 上阻塞。然后,不知何故在另一个线程中,我关闭了套接字。

在 Windows 上,这将解除对 recvfrom 的阻止,并且我的线程执行将终止。

在 Linux 上,这不会解除对 recvfrom 的阻塞,因此,我的线程永远不会做任何事情,并且线程执行不会终止。

任何人都可以帮助我了解 Linux 上发生的事情吗?当套接字关闭时,我希望 recvfrom 解除阻塞

我一直在阅读有关使用 select() 的信息,但我不知道如何在我的特定情况下使用它。

4

4 回答 4

17

调用shutdown(sock, SHUT_RDWR)套接字,然后等待线程退出。(即pthread_join)。

您会认为这close()会解除对 的阻止recvfrom(),但它不会在 linux 上。

于 2011-11-01T21:19:44.847 回答
6

Here's a sketch of a simple way to use select() to deal with this problem:

// 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
}

A more elegant method would be to avoid using the timeout feature of select, and instead create a socket pair (using socketpair()) and have the main thread send a byte on its end of the socket pair when it wants the I/O thread to go away, and have the I/O thread exit when it receives a byte on its socket at the other end of the socketpair. I'll leave that as an exercise for the reader though. :)

It's also often a good idea to set the socket to non-blocking mode also, to avoid the (small but non-zero) chance that the recvfrom() call might block even after select() indicated the socket is ready-to-read, as described here. But blocking mode might be "good enough" for your purpose.

于 2011-06-18T00:37:01.667 回答
4

不是答案,但 Linux 关闭手册页包含有趣的引用:

当同一进程中的其他线程中的系统调用可能正在使用文件描述符时,关闭文件描述符可能是不明智的。由于可以重用文件描述符,因此存在一些可能导致意外副作用的模糊竞争条件。

于 2011-06-17T18:25:12.037 回答
4

你在要求不可能的事。调用的线程根本不可能close知道另一个线程被阻塞在recvfrom. 尝试编写保证这种情况发生的代码,你会发现这是不可能的。

不管你做什么,调用总是有可能与调用close竞争recvfrom。调用close更改了套接字描述符所指的内容,因此它可以更改调用的语义recvfrom

进入的线程recvfrom无法以某种方式向调用它的线程发出信号,close表明它被阻塞(而不是即将阻塞或刚刚进入系统调用)。因此,实际上没有可能的方法来确保其行为是closerecvfrom预测的。

考虑以下:

  1. 一个线程即将调用recvfrom,但它被系统需要做的其他事情抢占了。
  2. 稍后,线程调用close.
  3. 由系统的 I/O 库调用socket并获得与您所使用的相同的描述符启动的线程close
  4. 最后,线程调用recvfrom,现在它从库打开的套接字接收。

哎呀。

永远不要做任何像这样的事情。当另一个线程正在或可能正在使用该资源时,不得释放该资源。时期。

于 2017-06-13T16:15:40.493 回答