【问题标题】:Safely close an indefinitely running thread安全关闭无限期运行的线程
【发布时间】:2014-11-11 14:49:06
【问题描述】:

首先,我意识到如果我的代码处于循环中,我可以在我希望线程关闭时使用do while 循环来检查变量集,但在这种情况下这是不可能的(看起来):

DWORD WINAPI recv thread (LPVOID random) {
    recv(ClientSocket, recvbuffer, recvbuflen, 0);
    return 1;
}

在上面,recv() 是一个阻塞函数。 (如果格式不正确,请原谅我。这是我在手机上能做的最好的事情。)

我将如何终止这个线程,因为它从不关闭但从不循环?

谢谢, ~P

【问题讨论】:

标签: multithreading sockets recv


【解决方案1】:

在其他解决方案中,您可以

a) 为套接字设置超时并通过在适当的循环中检查返回值和/或错误来正确处理超时:

setsockopt(ClientSocket,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(timeout))

b) 关闭套接字,recv(..) 从阻塞状态返回错误。

【讨论】:

    【解决方案2】:

    你可以在 recv() 之前使用 poll 来检查是否有东西要接收。

    struct pollfd poll;
    int res;
    
    poll.fd = ClientSocket;
    poll.events = POLLIN;
    res = poll(&poll, 1, 1000); // 1000 ms timeout
    
    if (res == 0)
    {
        // timeout
    }
    else if (res == -1)
    {
        // error
    }
    else
    {
        // implies (poll.revents & POLLIN) != 0
        recv(ClientSocket, recvbuffer, recvbuflen,0); // we can read ...
    } 
    

    【讨论】:

    • 我的编译器似乎不喜欢该代码。 call of an object of a class type without appropriate operator()我还不够先进,无法找到答案。具体来说,这个错误来自代码第六行的poll
    【解决方案3】:

    我处理这个问题的方法是永远不要在 recv() 内部阻塞——最好将套接字设置为非阻塞模式,但是当你知道套接字当前有一些字节可供读取。

    这就引出了下一个问题:如果你不在 recv() 内部阻塞,你如何防止 CPU 旋转?这个问题的答案是使用正确的参数调用 select()(或 poll()),这样你就会阻塞在那里,直到套接字有字节准备好接收 recv()。

    然后是第三个问题:如果您的线程现在在 select() 中被阻塞(可能永远),我们是不是又回到了原来的问题?不完全是,因为现在我们可以实现self-pipe trick 的变体。特别是,因为 select()(或 poll())可以同时“监视”多个套接字,我们可以告诉调用阻塞,直到两个套接字中的任何一个有数据可供读取。然后,当我们想要关闭线程时,主线程所要做的就是向第二个套接字发送一个字节的数据,这将导致 select() 立即返回。当线程看到是第二个socket准备好读取时,应该退出响应,这样主线程对WaitForSingleObject(theThreadHandle)的阻塞调用就会返回,然后主线程就可以清理干净了,没有任何风险竞争条件。

    最后一个问题是:如何设置一个套接字对,以便您的主线程可以在其中一个套接字上调用 send(),而您的 recv 线程将看到发送的数据出现在另一个套接字上?在 POSIX 下很容易,有一个 socketpair() 函数可以做到这一点。在 Windows 下,socketpair() 不存在,但您可以滚动您自己的实现,如 here 所示。

    【讨论】:

    • 这里提出的想法看起来很有希望。我会尽快尝试。 :)
    • 在 Windows 上,您不需要第二个套接字。那只会不必要地浪费一个端口。使用WSACreateEvent() 创建两个事件对象,使用WSAEventSelect() 将一个与真实套接字关联,然后使用WSAWaitForMultipleEvents() 等待这两个事件。当您需要终止时,使用WSASetEvent() 发出第二个事件的信号。 WSAWaitForMultipleEvents() 将告诉您发出了哪个事件。如果您确实使用第二个套接字,则不必向它发送数据。您可以使用shutdown() 关闭套接字的一侧,这将使另一侧进入准备读取状态。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-20
    • 2015-05-28
    • 1970-01-01
    • 2022-06-22
    • 1970-01-01
    • 2017-11-17
    相关资源
    最近更新 更多