【问题标题】:how to interrupt a thread which is waiting on recv function? [duplicate]如何中断正在等待recv函数的线程? [复制]
【发布时间】:2011-03-02 13:51:07
【问题描述】:

我有一个挂在 recv 函数上的套接字侦听器:

size_t recvLen = recv(sock, buf, 512, 0);

我想通过中断这个线程来终止它。 MSDN 说:

发出阻塞 Winsock 调用时 比如recv,Winsock可能需要等待 通话前的网络事件 可以完成。温索克执行 在这种情况下警惕等待, 这可以被打断 异步过程调用 (APC) 安排在同一个线程上。

我该怎么做?

【问题讨论】:

  • 通过将APC排队到线程来使用closesocket,这将导致recv返回错误,因为套接字已关闭

标签: c++ sockets


【解决方案1】:

您可以通过QueueUserAPC 将 APC 排队到它来中断它。但是,终止 APC 中的线程很可能是不安全的。排队 APC 不会结束recv,它只是中断它;一旦 APC 返回,它将再次回到等待recv

如果您想完全停止recv,您应该使用select 并设置超时以等待数据可用。然后,您可以检查是否应该继续等待数据或在每次超时时继续。

【讨论】:

  • 在 APC 调用中调用 closesocket() 它将返回,不需要异步套接字或邪恶的 TerminateThread 调用:)
【解决方案2】:

如果您不想接收更多数据,您可以随时终止套接字。只需调用 close() 就可以了,有问题的函数会立即返回错误。

我过去所做的只是运行另一个超时线程,如果没有设置“不要死”标志,则在等待期之后杀死套接字。

【讨论】:

    【解决方案3】:

    我认为在recv 之前检查套接字缓冲区更灵活,而不是覆盖很多select() 支持。您可以调用ioctlsocket(SockHandle, FIONREAD, Longint(CountInBuffer)) 来查看网络缓冲区中是否有要读取的数据,然后调用recv(SockHandle, buff, CountInBuffer, 0)。这样,如果使用 CountInBuffer 为缓冲区本身分配了足够多的缓冲区,则可以进行一次 recv 调用来读取整个网络读取缓冲区。否则需要循环调用recv来读取网络缓冲区,这是传统的方式。在这两种情况下,您仍然受到CountInBuffer 的约束。

    【讨论】:

      【解决方案4】:

      我认为处理这个问题的最好方法是将套接字置于非阻塞 I/O 模式,这样线程就不会在 recv() 中阻塞(或者在 send() 中)。线程应该只在 select()(或 WaitMultipleObjects())内阻塞。这样,如果数据到达套接字,则 select()(或 WaitMultipleObjects())调用将返回(在这种情况下,您可以调用 recv() 以获取新数据而不会阻塞),但您也可以使用 select()/ WaitMultipleObjects() 在其他事情发生时返回;例如当它从主线程得到提示时。如果您使用 select(),则该提示可以是主线程在不同的套接字对上发送一个字节(主线程持有套接字对的一端,而 I/O 线程持有另一端);如果您使用的是 WaitMultipleObjects(),那么我相信您可以使用任何会导致 WaitMultipleObjects() 返回的标准 Windows 事件/信号方法。

      【讨论】:

      • 您无需将套接字置于非阻塞模式即可使用select()。您可以将select() 与阻塞套接字一起使用。只需在调用recv() 之前调用select() 确保套接字是可读的,那么即使套接字处于阻塞模式,`recv() 也不会阻塞。
      • 我不确定这比将套接字置于非阻塞模式更容易/更好。现在,您不必添加一行代码,而是必须在每次对套接字的潜在阻塞调用之前添加一行代码(如果您或未来的代码维护者忘记这样做,您将有一个微妙而艰难的检测程序中可能需要花费大量时间来追踪的错误)
      • 另外,check-with-select-before-recv() 方法并不总是可靠地工作:stackoverflow.com/a/5352634/131930
      【解决方案5】:

      Errrr... 在同一个线程上执行 APC? :-))

      不过,说真的,目前尚不清楚您的目标是什么。
      如果您只想结束线程,请使用TerminateThread 函数。
      如果你想中断这个特定的调用,你可以关闭套接字。

      【讨论】:

      • 不应使用终止线程,因为它不会通知 DLL 并且不会让线程清理。关闭套接字是可能的,但您可能仍然需要套接字。
      【解决方案6】:

      真正中断阻塞的recv() 调用并使其完全退出的唯一方法是从另一个线程上下文而不是被阻塞的线程上下文关闭套接字。如果这不是一个选项,那么您需要重新编写您的套接字逻辑。最好使用非阻塞或异步 I/O,这样recv()(或后者的WSARecv())将永远不会阻塞,并且您可以在阅读时做任何您需要做的事情(例如检查线程终止条件)在后台执行。

      【讨论】:

      • 只要确保您没有竞争条件,即其他线程可能已经为您关闭它,或者您可能正在关闭为其他套接字(新套接字)打开的套接字描述符[?]
      • re: async 显然可以使用 WaitForMultipleObjects ffmpeg.org/pipermail/ffmpeg-devel/2013-December/152211.html
      • 您不能将任何WaitFor...Object(s) 函数与套接字句柄一起使用。您将不得不使用来自CreateEvent() 的事件句柄,当套接字操作完成时调用SetEvent()。或者将WSACreateEvent()WSAEventSelect()WSAWaitForMultipleEvents() 一起使用。或者使用CreateIoCompletionPort()GetQueuedCompletionStatus()
      猜你喜欢
      • 2014-08-22
      • 2016-05-25
      • 2023-03-08
      • 1970-01-01
      • 1970-01-01
      • 2020-04-12
      • 1970-01-01
      • 2016-11-16
      • 1970-01-01
      相关资源
      最近更新 更多