多个线程同时调用WSAStartup()会导致死锁吗?

5
我正在开发一款应用程序,其中包括一个TCP服务器和几个UDP服务器/监听器。每个服务器都是一个独立的线程,与建立的TCP连接的工作线程相同。我在每个线程中调用WSAStartup()。
有时候,调用WSAStartup()会挂起(对我来说看起来像死锁)。以下是堆栈跟踪:
  ntdll.dll!_KiFastSystemCallRet@0()  
  ntdll.dll!_ZwWaitForSingleObject@12()  + 0xc bytes 
  ntdll.dll!_RtlpWaitForCriticalSection@4()  + 0x8c bytes 
  ntdll.dll!_RtlEnterCriticalSection@4()  + 0x46 bytes 
  ntdll.dll!_LdrpGetProcedureAddress@20()  + 0x17d bytes 
  ntdll.dll!_LdrGetProcedureAddress@16()  + 0x18 bytes 
  kernel32.dll!_GetProcAddress@8()  + 0x3e bytes 
  vld.dll!03203723()  
  [Frames below may be incorrect and/or missing, no symbols loaded for vld.dll] 
  ws2_32.dll!CheckForHookersOrChainers()  + 0x22 bytes 
  ws2_32.dll!_WSAStartup@8()  + 0xa7 bytes 

这个死锁发生在初始化阶段。我发现TCP服务器已经启动并建立了一个TCP连接,但只有一个UDP服务器被启动。堆栈跟踪来自应该启动其余UDP服务器的函数。我的猜测是,在我尝试初始化UDP服务器并调用WSACStartup()时,另一个线程正在处理另一个套接字操作,例如一个新的TCP连接,并且它也在调用WSAStartup()?

我的问题是,从几个线程调用WSAStartup()会导致这个死锁吗? 此外,我检查了是否在死锁之前调用了WSACleanup(),但没有。执行永远不会到达任何WSACleanup()。

我知道只需要调用一次WSAStartup就足够了,但是多次调用WSAStartup()不应该成为问题(MSDN]1): “如果需要多次获取WSADATA结构信息,则应用程序可以多次调用WSAStartup。” 因此,我想确定这个死锁是由WSAStartup()还是其他原因引起的。


这不是对你问题的回答,但是你考虑过使用boost asio (http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio.html)吗?我使用那个库解决类似你的问题要容易得多。 - nabulke
Nikolai,我以前使用过boost并且很喜欢它。自从我开始用WinSock开发这个应用程序时,我想深入了解这个问题的根本原因。只是出于好奇吧 :) - Misko Mare
3
看起来你遇到了涉及加载器锁的死锁问题;你应该查看其他线程并检查它们是否正在调用LoadLibrary、GetProcAddress等函数,特别是如果在它们的DllMain函数中发生这种情况。 - Luke
dauphic,我正在使用VS 2005。我不知道如何在VS 2005中检查其他线程的堆栈。我相信更新版本的VS应该有这个选项。 - Misko Mare
死锁不是由于从多个线程调用WSAStartup引起的。而是由于从DllMain(或者你决定称之为DLL入口点的任何函数)中调用它。这已经被@Luke指出,但由于你决定忽略它,我想再次提出来。 - IInspectable
显示剩余2条评论
4个回答

4

3

您无需多次调用 WSAStartup()。每个程序只需要调用一次即可。


1
依我看,问题表述很清楚,话题发起人已经阅读了MSDN的这一部分内容,并且他正试图找到问题的根本原因,而不是采用“避免”该问题的解决方案(这绝对是一个好的解决方案)。 - Andrey
这就像是一个老笑话:“医生,我这样做会疼。”“那就别这样做呗。” - Warren Young
2
如果您正在使用AppVerifier,则每个进程仅调用一次WSAStartup是不够的。最好的做法是让每个线程初始化Windows套接字,并在完成后取消初始化。特别是如果您无法提前知道哪个是最后一个退出的线程,这一点尤为重要。 - IInspectable

1

我认为Luke是正确的。您不能在DllMain()或全局/静态变量的初始化程序中调用WSAStartup()。更改代码,使其不会发生。


我正在开发一个独立应用程序,使用系统DLL,因此我没有DLLMain()。所有对WSAStartup()的调用都在线程函数中: http://msdn.microsoft.com/en-us/library/ms686736(VS.85).aspx附加库列表如下:
  • ws2_32.lib
  • strsafe.lib
  • shell32.lib
- Misko Mare
仔细查看已加载到应用程序“release”版本中的DLL列表(无剖析器、泄漏检测器等)。很有可能其中一个DLL会捕获Windows函数。SysInternals的Process Explorer将对您有很大帮助。 - Andrey

0

WSAStartup 实际上并不会导致任何类型的 LoadLibrary,因此我觉得这不是一个 loader lock 的情况。

相反,很明显 Windows API 在你的情况下被拦截了(这里用术语 trap 更好,因为在 Windows 中 hook 有其他含义)。

因此,我认为问题不在于并发使用 WSAStartup,而在于第三方陷阱对进程中原始 Windows API 函数的副作用。我认为,你需要清理你的环境,摆脱任何外部影响(来自你方或反病毒软件的 api 陷阱等)。

顺便说一句,确保你的每个线程都为 WSAStartup 提供其自己单独的 WSADATA 输出参数副本。


1
你确定它没有调用 LoadLibrary 吗? - notbad.jpeg
你说得对,我不记得当时为什么会这样想,但根据MSDN,它实际上可以加载辅助DLL,因此加载器锁可能是一个问题。从堆栈来看,我仍然认为根本原因在于拦截WSAStartup的陷阱。 - Andrey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接