用VS2008创建一个规则DLL(共享MFC)用到多线程的问题

DDGG 2010-04-22 06:33:27
一个MFC规则DLL,当DLL第一次被加载的时候(DLL_PROCESS_ATTACH即CWinApp::InitInstance())建立一个线程,想在DLL卸载时通过一个标志变量m_bExiting停止它并进行清理工作,但是— —

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对话框程序,使用隐式加载。
...全文
588 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
不说害怕 2010-07-09
  • 打赏
  • 举报
回复

我犯了这样的错误..
虽然以前知道一点,但还是想这么干.
SiGoYi 2010-04-23
  • 打赏
  • 举报
回复
up~~~~~~~~~~~~~~~~
unituniverse2 2010-04-23
  • 打赏
  • 举报
回复
楼上说得不大对。楼主的线程是用win32 api创建的,mfc不知道也不会去收集他的线程。事实上线程是在DllMain之前被系统结束了。这个不是bug,而是系统就是这样设计的
DDGG 2010-04-23
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 songtao_01 的回复:]
1.
UINT WINAPI CTransmitSDKApp::ThreadFunc( CTransmitSDKApp *pThis )是静态函数吗?
如果要把线程函数放到类里面,貌似要设置成静态函数吧
[/Quote]
是的,是静态函数,声明的时候加了static关键字。

[Quote=引用 5 楼 songtao_01 的回复:]
不知道是你笔误,还是什么
DWORD dwThreadId;
HANDLE hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadFunc, this, 0, &dwThreadId);
这句编译不通过,HANDLE hThread = ::CreateThread(NULL, 0, ThreadFunc, this, 0, &dwThreadId);
这样才通过
[/Quote]
这个我用的是VS2008 SP1,我贴的代码都是编译通过可以运行的。

[Quote=引用 5 楼 songtao_01 的回复:]
1.
UINT WINAPI CTransmitSDKApp::ThreadFunc( CTransmitSDKApp *pThis )是静态函数吗?
如果要把线程函数放到类里面,貌似要设置成静态函数吧
[/Quote]
是的,是静态函数,声明的时候加了static关键字。

[Quote=引用 5 楼 songtao_01 的回复:]
3.
刚开始执行ExitInstance的时候,线程并没有结束
[/Quote]
我在ExitInstance()函数的第一条语句 m_bExiting = TRUE; 设置了断点,当调试器停下来的时候查看线程窗口,那个新建的线程确实已经不在了。可能你的编译器不是VS2008?

[Quote=引用 5 楼 songtao_01 的回复:]
4.
不能执行到线程函数的return的原因,可能是程序一口气把ExitInstance执行完了,并另外执行了强制关闭线程的代码
要执行线程里面的return,你必须在ExitInstance里面加WaitForSingleObject等待一个事件,然后在线程return的时候,SetEvent一下。
[/Quote]
确实是要这么干的,只是暂时为了简化问题代码而省略了:)

[Quote=引用 5 楼 songtao_01 的回复:]
5.
m_bExiting = TRUE;多线程,最好不要这样写,应该用InterlockedExchange
[/Quote]
我一直以为对简单变量的赋值是原子操作,以后如果遇到问题的话再改用InterlockedExchange试试,哈哈!


unituniverse2给的网页我正在看,还没看完
songtao_01 2010-04-23
  • 打赏
  • 举报
回复
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
DDGG 2010-04-23
  • 打赏
  • 举报
回复
是的,我的系统是Win7。

好了这个问题我已经搞清楚了,谢谢大家,结帖!
unituniverse2 2010-04-23
  • 打赏
  • 举报
回复
首先你要清楚的是,原则上windows系统会被设计成假定各模块的DllMain被调用时是没有顺序关联的,并且在模块仅有唯一线程的前提下,模块之间不再有关联。并且要求你也按此假定编程。这并不是说调用时DllMain没有固定的顺序(事实上总有),而是说,你不应该自己假定顺序,即使你获得了这个顺序,也不能说将来微软仍然会遵守这个顺序。总之必须保证DllMain不能嵌套
比如不能在DllMain里调用LoadLibrary和FreeLibrary。(我发现现在国内不少软件,甚至是大牌软件还在这样调用)
而且在dll装载和卸载阶段,除了DllMain和被其调用的代码“可执行”外,用户必须保证它的代码中没有其他线程。也许你会问,我线程和DllMain嵌套有什么关系呀,你可以看下DllMain的参数里还有DLL_THREAD_xxxx标志,这标志是干什么用的?这就是原因。总之建议你遵循文档开发,不能从文档里推导出的都是不能当作正确的。无法保证兼容性(就像前面用2005开发的,和2008行为就不一样。不过我更愿意相信是他的系统版本和你不同)。至少Windows7就是在调用DllMain之前主动回收掉了除主线程外的所有线程。也可保证DLL_PROCESS_DETACH在所有的DLL_THREAD_DETACH调用完后才被调用到。
DDGG 2010-04-23
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 songtao_01 的回复:]

引用 7 楼 unituniverse2 的回复:

楼上说得不大对。楼主的线程是用win32 api创建的,mfc不知道也不会去收集他的线程。事实上线程是在DllMain之前被系统结束了。这个不是bug,而是系统就是这样设计的

我用vs2005试了一下,执行ExitInstance没有被结束。真的,我在线程里面用TRACE打印,他一直还在打印。至于楼主说的在线程窗口没有看到,是不是你……
[/Quote]

是的,我调试的是exe,但是当我把dll设为启动项目,测试结果还是和之前一致,运行到ExitInstance()的时候在线程窗口只剩下主线程。

在线程中用TRACE()循环输出一个自增的整数,然后ExitInstance()的开头加上Sleep(10000),后面设断点,运行程序->关闭对话框,调试窗口里TRACE()信息停止,10秒后在断点处停止了下来。

unituniverse2 兄给的文章里有一段话是:
When handling DLL_PROCESS_DETACH, a DLL should free resources such as heap memory only if the DLL is being unloaded dynamically (the lpReserved parameter is NULL). If the process is terminating (the lpvReserved parameter is non-NULL), all threads in the process except the current thread either have exited already or have been explicitly terminated by a call to the ExitProcess function, which might leave some process resources such as heaps in an inconsistent state.

是不是说当程序退出时,DLL_PROCESS_DETACH发生的时刻(CWinApp::ExitInstance()被调用),在此之前进程已经从ExitProcess()返回,因此除了主线程之外的其它线程都已经主动或强制终止了?
songtao_01 2010-04-23
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 unituniverse2 的回复:]

楼上说得不大对。楼主的线程是用win32 api创建的,mfc不知道也不会去收集他的线程。事实上线程是在DllMain之前被系统结束了。这个不是bug,而是系统就是这样设计的
[/Quote]
我用vs2005试了一下,执行ExitInstance没有被结束。真的,我在线程里面用TRACE打印,他一直还在打印。至于楼主说的在线程窗口没有看到,是不是你调试的是exe而不是dll啊。我很少用线程窗口去看,我一般都是在调试窗口看。
unituniverse2 2010-04-22
  • 打赏
  • 举报
回复
既然是dll,这是不允许的。可以看
http://msdn.microsoft.com/en-us/library/ms682583(VS.85).aspx
里面的remark。内容比较多,但该注意的问题都说了
DDGG 2010-04-22
  • 打赏
  • 举报
回复
CTransmitSDKApp 对象是这个DLL的全局对象啊,你可以建一个MFC规则DLL项目,项目名称用 TransmitSDK。

TransmitSDK.h
// 唯一的一个 CTransmitSDKApp 对象

CTransmitSDKApp theApp;

unituniverse2 2010-04-22
  • 打赏
  • 举报
回复
你有义务让你的CTransmitSDKApp对象在线程终止前持续存在着!
unituniverse2 2010-04-22
  • 打赏
  • 举报
回复
估计你的CTransmitSDKApp对象是在线程创建以后就销毁了对吧?然后那个对象的成员m_bExiting所在的内存变成了垃圾值(调试时可以看到0xcccccccc,或0xfeeefeee)。

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧