2014-09-24 75 views
2

我有一个用C++编写的Windows服务,它用作侦听传入连接的TCP服务器。在Windows C++服务中阻塞两个线程

我初始化服务器套接字并将accept代码放在一个单独的线程中。这将接受并处理传入的连接。

但是,如果服务收到STOP信号,我还需要停止该线程。所以我想用CreateEvent创建一个事件对象,然后等待它发出信号。这种等待会发生在创建接受线程的线程中。所以我可以使用TerminateThread函数在接收到STOP信号时停止接受线程。

然而,MSDN

TerminateThread是只能在最极端的情况下使用危险的功能。

这应该如何严格遵守并且我的方法正确?还有什么可以做到这一点?

+0

非常严格。在你的情况下,我认为你可以使用CancelSynchronousIo来取消accept()函数。 – 2014-09-24 06:36:17

回答

2

在Windows中,只需调用closesocket即可唤醒来自其他线程的阻止accept调用。阻塞accept调用将返回-1,并且您的代码有机会通过检查您已经设置的某个其他退出条件(例如全局变量)来突破它所处的任何循环。

这也适用于Mac(和可能的BSD衍生物)与close函数,但不是Linux。对此问题更通用的UNIX解决方案是here

以下Windows解决方案的一些pseduo代码。

SOCKET _listenSocket; 
bool _needToExit = false; 
HANDLE _hThread; 

void MakeListenThreadExit() 
{ 
    _needToExit = true; 
    closesocket(_listenSocket); 
    _listenSocket = INVALID_SOCKET; 

    // wait for the thread to exit 
    WaitForSingleObject(_hThread, INFINITE);  
} 

DWORD __stdcall ListenThread(void* context) 
{ 
    while (_needToExit == false) 
    {    
     SOCKET client = accept(_listenSocket, (sockaddr*)&addr, &addrSize); 
     if ((client == -1) || _needToExit) 
     { 
      break; 
     } 
     ProcessClient(client); 
    }  
    return 0; 
} 
+0

从未想过这个!感谢您的解决方案。 – Cygnus 2014-09-24 07:09:42

+0

这不起作用,不应该使用。您有以下竞争条件:您即将打电话'接受'。你调用'closesocket'。另一个线程(可能在你不控制的库中)分配一个套接字并获得刚刚关闭的相同描述符。您现在在错误的*套接字上调用accept,并从库中窃取连接。当你开始调用'closesocket'时,根本没有办法确保你仍然被'accept'阻塞。 – 2014-09-24 08:13:26

+0

@DavidSchwartz - 它实际上**做**工作。至于它是否应该被用于辩论。 OP应仔细考虑你的观点。我打算仔细检查,但我相当肯定Win32使用递增的套接字句柄标识符或合理的算法来确保套接字句柄值不会被快速重用 - 用于处理这些类型的错误。在你描述的竞态条件中,你必须在竞争条件内创建(2^32)-1套接字句柄以获得先前关闭的重复套接字句柄。 – selbie 2014-09-24 08:32:20

1

在这种情况下,请勿在阻塞套接字上使用accept()。改为使用非阻塞套接字。然后,您可以使用select()并超时,以便您的线程可以定期检查终止条件。或者更好的是,使用WSACreateEvent()WSASelectEvent()。创建两个事件对象,一个用于检测客户端连接,另一个用于检测线程终止。然后您可以使用WSAWaitForMultipleEvents()同时等待两个事件。使用WSASetEvent()可在需要时发出终止事件的信号,并且每当发出其他事件时,请致电accept()WSAAccept()WSAWaitForMultipleEvents()会告诉你要采取哪一个事件。