用VS2008创建一个规则DLL(共享MFC)用到多线程的有关问题
用VS2008创建一个规则DLL(共享MFC)用到多线程的问题
一个MFC规则DLL,当DLL第一次被加载的时候(DLL_PROCESS_ATTACH即CWinApp::InitInstance())建立一个线程,想在DLL卸载时通过一个标志变量m_bExiting停止它并进行清理工作,但是— —
DLL的调用代码是一个MFC对话框程序,使用隐式加载。
------解决方案--------------------
既然是dll,这是不允许的。可以看
http://msdn.microsoft.com/en-us/library/ms682583(VS.85).aspx
里面的remark。内容比较多,但该注意的问题都说了
------解决方案--------------------
1.
UINT WINAPI CTransmitSDKApp::ThreadFunc( CTransmitSDKApp *pThis )是静态函数吗?
如果要把线程函数放到类里面,貌似要设置成静态函数吧
2.
不知道是你笔误,还是什么
DWORD dwThreadId;
HANDLE hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadFunc, this, 0, &dwThreadId);
这句编译不通过,HANDLE hThread = ::CreateThread(NULL, 0, ThreadFunc, this, 0, &dwThreadId);
这样才通过
3.
刚开始执行ExitInstance的时候,线程并没有结束
4.
不能执行到线程函数的return的原因,可能是程序一口气把ExitInstance执行完了,并另外执行了强制关闭线程的代码
要执行线程里面的return,你必须在ExitInstance里面加WaitForSingleObject等待一个事件,然后在线程return的时候,SetEvent一下。
5.
m_bExiting = TRUE;多线程,最好不要这样写,应该用InterlockedExchange
------解决方案--------------------
首先你要清楚的是,原则上windows系统会被设计成假定各模块的DllMain被调用时是没有顺序关联的,并且在模块仅有唯一线程的前提下,模块之间不再有关联。并且要求你也按此假定编程。这并不是说调用时DllMain没有固定的顺序(事实上总有),而是说,你不应该自己假定顺序,即使你获得了这个顺序,也不能说将来微软仍然会遵守这个顺序。总之必须保证DllMain不能嵌套。
比如不能在DllMain里调用LoadLibrary和FreeLibrary。(我发现现在国内不少软件,甚至是大牌软件还在这样调用)
而且在dll装载和卸载阶段,除了DllMain和被其调用的代码“可执行”外,用户必须保证它的代码中没有其他线程。也许你会问,我线程和DllMain嵌套有什么关系呀,你可以看下DllMain的参数里还有DLL_THREAD_xxxx标志,这标志是干什么用的?这就是原因。总之建议你遵循文档开发,不能从文档里推导出的都是不能当作正确的。无法保证兼容性(就像前面用2005开发的,和2008行为就不一样。不过我更愿意相信是他的系统版本和你不同)。至少Windows7就是在调用DllMain之前主动回收掉了除主线程外的所有线程。也可保证DLL_PROCESS_DETACH在所有的DLL_THREAD_DETACH调用完后才被调用到。
一个MFC规则DLL,当DLL第一次被加载的时候(DLL_PROCESS_ATTACH即CWinApp::InitInstance())建立一个线程,想在DLL卸载时通过一个标志变量m_bExiting停止它并进行清理工作,但是— —
- C/C++ code
BOOL CTransmitSDKApp::InitInstance() { CWinApp::InitInstance(); m_bExiting = FALSE; DWORD dwThreadId; HANDLE hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadFunc, this, 0, &dwThreadId); return TRUE; // <-- 在这里设断点,看到新建的线程已经起来了。 } int CTransmitSDKApp::ExitInstance() { m_bExiting = TRUE; // <-- 在这里设断点,发现创建的线程已经没有了。已经被MFC强制终止了?? return CWinApp::ExitInstance(); } UINT WINAPI CTransmitSDKApp::ThreadFunc( CTransmitSDKApp *pThis ) { while (!pThis->m_bExiting) { Sleep(1); } return 0; // <-- 在这里设断点,永远不会运行到这里。 }
DLL的调用代码是一个MFC对话框程序,使用隐式加载。
------解决方案--------------------
既然是dll,这是不允许的。可以看
http://msdn.microsoft.com/en-us/library/ms682583(VS.85).aspx
里面的remark。内容比较多,但该注意的问题都说了
------解决方案--------------------
1.
UINT WINAPI CTransmitSDKApp::ThreadFunc( CTransmitSDKApp *pThis )是静态函数吗?
如果要把线程函数放到类里面,貌似要设置成静态函数吧
2.
不知道是你笔误,还是什么
DWORD dwThreadId;
HANDLE hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadFunc, this, 0, &dwThreadId);
这句编译不通过,HANDLE hThread = ::CreateThread(NULL, 0, ThreadFunc, this, 0, &dwThreadId);
这样才通过
3.
刚开始执行ExitInstance的时候,线程并没有结束
4.
不能执行到线程函数的return的原因,可能是程序一口气把ExitInstance执行完了,并另外执行了强制关闭线程的代码
要执行线程里面的return,你必须在ExitInstance里面加WaitForSingleObject等待一个事件,然后在线程return的时候,SetEvent一下。
5.
m_bExiting = TRUE;多线程,最好不要这样写,应该用InterlockedExchange
------解决方案--------------------
首先你要清楚的是,原则上windows系统会被设计成假定各模块的DllMain被调用时是没有顺序关联的,并且在模块仅有唯一线程的前提下,模块之间不再有关联。并且要求你也按此假定编程。这并不是说调用时DllMain没有固定的顺序(事实上总有),而是说,你不应该自己假定顺序,即使你获得了这个顺序,也不能说将来微软仍然会遵守这个顺序。总之必须保证DllMain不能嵌套。
比如不能在DllMain里调用LoadLibrary和FreeLibrary。(我发现现在国内不少软件,甚至是大牌软件还在这样调用)
而且在dll装载和卸载阶段,除了DllMain和被其调用的代码“可执行”外,用户必须保证它的代码中没有其他线程。也许你会问,我线程和DllMain嵌套有什么关系呀,你可以看下DllMain的参数里还有DLL_THREAD_xxxx标志,这标志是干什么用的?这就是原因。总之建议你遵循文档开发,不能从文档里推导出的都是不能当作正确的。无法保证兼容性(就像前面用2005开发的,和2008行为就不一样。不过我更愿意相信是他的系统版本和你不同)。至少Windows7就是在调用DllMain之前主动回收掉了除主线程外的所有线程。也可保证DLL_PROCESS_DETACH在所有的DLL_THREAD_DETACH调用完后才被调用到。