重工电子论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 活动 交友 discuz
查看: 5057|回复: 3
打印 上一主题 下一主题

[VC] 【MFC多线程】调用MsgWaitForMultipleObjects 函数的相关问题

[复制链接]

287

主题

668

帖子

5636

积分

学生管理组

Rank: 8Rank: 8

积分
5636
跳转到指定楼层
楼主
发表于 2015-6-16 23:39:09 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

这个问题来自上一个帖子http://www.cqutlab.cn/thread-64-1-1.html
上一个问题帖子的解决办法就是这个帖子的做法,但是又遇到新问题。。。

下面是我的代码, 然后再引出问题:
这是一个基于对话框的程序,
编译环境VC6  SP6
运行环境   XP SP3
首先在头文件里面有几个变量的声明:
[C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode
	BOOL runflag;		//这个为否正在发送数据的标志
	int uiButton;		//这个是表明哪一个函数启动的发送线程 的标志
	
	HANDLE m_CANEvent;
	HANDLE m_CANSendThreadHandle[3];	//这里定义3个handle 实际只用一个
	CWinThread *pCANThreads;	


下面是CPP里面的代码
[C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode
BOOL CExample2Dlg::OnInitDialog()
{
	CDialog::OnInitDialog();
…
…
	m_CANEvent=CreateEvent(NULL,FALSE,FALSE,NULL);	//声明一个事件
	runflag=0;	//线程运行标志置0
	
	return TRUE;  // return TRUE  unless you set the focus to a control
}	

void CExample2Dlg::OnButton1()	//这是一个按钮响应函数,用按钮开启线程的函数
{
	DWORD result ;
    MSG msg ; 
	if(runflag==1)		//如果线程正在运行
	{
		SetEvent(m_CANEvent);	//发出让线程结束
		while(1)
		{
			result=MsgWaitForMultipleObjects(1,m_CANSendThreadHandle,FALSE,INFINITE,QS_ALLINPUT); 
			if   (result == WAIT_OBJECT_0)	//如果等到线程结束 就执行下面的break;
			{
			   break;
			}  
			else  
			{
				while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))	//线程没有结束 就分发消息
				{
					TRACE("收到消息,函数返回值为%d /n",result);
					TranslateMessage(&msg);
					DispatchMessage(&msg);
				}			
}  
		}
	}
	ResetEvent(m_CANEvent);		//设置无信号,让线程进入等待超时的工作
	uiButton=1;				//标记这是按钮响应的函数启动的线程
	pCANThreads=AfxBeginThread(MyThread1,this);
	m_CANSendThreadHandle[0]=pCANThreads->m_hThread;	//赋值handle
	runflag=1;
	Sleep(1000);		//故意延迟1秒 让开启的线程跑一跑
}

UINT CExample2Dlg::MyThread1(void *param)
{
	CExample2Dlg *Dlg=(CExample2Dlg *)param;
	int i;
	CString tmp;
	DWORD dw;
//在进入下面循环前,在我那个真实程序前,还有不少Dlg->XXXX的工作,在线程里面操作主界面的一//些显示,读取数据等工作

	for(i=0;i<100000;i++)		//这里是我人为模拟 要让它循环这么多次,意为模拟发送这么多次
	{
		dw = WaitForSingleObject(Dlg->m_CANEvent,10); //让其延迟10ms就输出一个数
		if( dw == WAIT_OBJECT_0 )	//收到结束线程信号
		{
			Dlg->runflag=0;		//线程运行标志复位
			return 0;
		}
		if( dw == WAIT_TIMEOUT)		//延迟到了,就执行下面的显示数值
		{
			if(Dlg->uiButton == 1)
			{
				tmp.Format(" %d ",i);
				Dlg->m_count.SetWindowText(tmp);	//这个是让其在界面上面显示当前i的数值
			}									// Dlg->m_count是主界面上面的一个static控件
			if(Dlg->uiButton ==2 )
			{
				tmp.Format(" %d ",i);
				Dlg->m_count2.SetWindowText(tmp);	//这个是让其在界面上面显示当前i的数值
			}									// Dlg->m_count是主界面上面的一个static控件
		}
	}
	
	Dlg->runflag=0;		//线程运行标志复位
	return 0;
}

void CExample2Dlg::OnButton2() 		//停止线程的按钮
{
	// TODO: Add your control notification handler code here
	SetEvent(m_CANEvent);
}


基于上面的代码,我第一次点Button1,那么就正常跑线程MyThread1,跑着跑着,我再点一次Button1,就会执行Button1下面if(runflag==1){…}里面的代码,然后线程MyThread1就自动退出了,可是现在的result=MsgWaitForMultipleObjects(1,m_CANSendThreadHandle,FALSE,INFINITE,QS_ALLINPUT); 返回的result 不会变为result == (WAIT_OBJECT_0)   也就是说,现在就进入死循环,一直在while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))那里循环了,

问题:为什么他在线程结束后 MsgWaitForMultipleObjects不会返回WAIT_OBJECT_0  ?而且我是确定线程是退出了的,在debug窗口里面看到了 我最先以为是MsgWaitForMultipleObjects第二个参数的问题,因为本身线程的那个HANDLE (pCANThreads->m_hThread;)会在线程结束的时候消失,但是我这里是在AfxBeginThread过后就赋值给m_CANSendThreadHandle[0]这个数组了,MsgWaitForMultipleObjects里面参数也是m_CANSendThreadHandle啊,

现在最大的问题就是 为什么MsgWaitForMultipleObjects 检测不到线程退出的信号?
下面是我第二次点击Button1的DEBUG窗口截图:

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

287

主题

668

帖子

5636

积分

学生管理组

Rank: 8Rank: 8

积分
5636
沙发
 楼主| 发表于 2015-6-16 23:46:54 | 只看该作者
我做了以下追踪改动

[C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode
                        else if( WAIT_OBJECT_0 + 1)
                        {
                           while (PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))
                           {        
                                        TRACE("收到消息,函数返回值为%d,进入消息分发 \n",result);
                                        TRACE("message里面消息值 %d \n",msg.message);
                                        TranslateMessage(&msg);
                                        DispatchMessage(&msg);
                           }
                                
                        }


DEBUG里面调试信息是 :
[C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode
收到消息,函数返回值为1,进入消息分发 
message里面消息值 512




我在button1里面做了以下改动 能够正常运行了 但是MsgWaitForMultipleObjects任然不能正常工作,也就是不能收到线程退出的消息  
[C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode

BOOL CExample2Dlg::OnInitDialog()
{
    CDialog::OnInitDialog();
…
…
    m_CANEvent=CreateEvent(NULL,FALSE,FALSE,NULL);    //声明一个事件
    runflag=0;    //线程运行标志置0
     
    return TRUE;  // return TRUE  unless you set the focus to a control
}  

void CExample2Dlg::OnButton1()        //用按钮开启线程的函数
{
        // TODO: Add your control notification handler code here3
        DWORD result ;
    MSG msg ; 
        if(runflag==1)                //如果线程正在运行
        {
                SetEvent(m_CANEvent);        //发出让线程结束
                TRACE("发送SetEvent信号,让线程结束 \n");
                while (TRUE)  
                {  
                        //wait for m_hThread to be over,and wait for  
                        //QS_ALLINPUT(Any message is in the queue)  
                        DWORD dwRet = MsgWaitForMultipleObjects (1, m_CANSendThreadHandle,   FALSE, INFINITE, QS_ALLINPUT);  
                        switch(dwRet)  
                        {  
                        case WAIT_OBJECT_0:   
                                {
                                        TRACE("MsgWaitForMultipleObjects 返回值 %ld ",dwRet);
                                        TRACE("从WAIT_OBJECT_0消息退出while循环 \n");
                                }
                                break; //break the loop  
                        case WAIT_OBJECT_0 + 1:  
                                //get the message from Queue  
                                //and dispatch it to specific window  
                                {
                                        TRACE("MsgWaitForMultipleObjects 返回值 %ld ",dwRet);
                                        TRACE("说明返回WAIT_OBJECT_0 + 1 ,分发消息,继续while循环\n");
                                        PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);  
                                        DispatchMessage(&msg);   
                                }
                                continue;  
                        default:
                                {
                                        TRACE("MsgWaitForMultipleObjects 返回值 %ld ",dwRet);
                                        TRACE("从default退出循环 \n");
                                }
                                break; // unexpected failure  
                        }  
                        break; 
                }
        }
        ResetEvent(m_CANEvent);                //设置无信号,让线程进入等待超时的工作
        uiButton=1;                                //标记这是按钮响应的函数启动的线程
        pCANThreads=AfxBeginThread(MyThread1,this);
        m_CANSendThreadHandle[0]=pCANThreads->m_hThread;
        runflag=1;
        Sleep(1000);

}

UINT CExample2Dlg::MyThread1(void *param)
{
        TRACE("MyThread1 Begin\n");
        CExample2Dlg *Dlg=(CExample2Dlg *)param;
        
        int i;
        CString tmp;
        DWORD dw;

        for(i=0;i<100000;i++)
        {
                dw = WaitForSingleObject(Dlg->m_CANEvent,10); //让其延迟10ms就输出一个数
                if( dw == WAIT_OBJECT_0 )        //收到结束线程信号
                {
                        Dlg->runflag=0;                //线程运行标志复位
                        TRACE("MyThread1 End 1\n");
                        return 0;                        //退出

                }
                if( dw == WAIT_TIMEOUT)                //延迟到了,就执行下面的显示数值
                {
                        if(Dlg->uiButton == 1)
                        {
                                tmp.Format(" %d ",i);
                                Dlg->m_count.SetWindowText(tmp);        //这个是让其在界面上面显示当前i的数值
                        }
                        if(Dlg->uiButton ==2 )
                        {
                                tmp.Format(" %d ",i);
                                Dlg->m_count2.SetWindowText(tmp);        //这个是让其在界面上面显示当前i的数值
                        }
                }
        }
        
        Dlg->runflag=0;                //线程运行标志复位
        TRACE("MyThread1 End\n");
        return 0;
}



整个过程是这样的
第一次点button1: 线程开始,开始正常运行
第二次点button1: 因为执行SetEvent(m_CANEvent); 所以线程MyThread1结束 ,然后MsgWaitForMultipleObjects收到消息,(WAIT_OBJECT_0 + 1 ) 然后MsgWaitForMultipleObjects再次收到消息 ,是default消息 然后从default退出while循环  而非从(收到消息,WAIT_OBJECT_0)退出循环

然后再执行button1下面的开启线程操作,这个时候也能正常开启,因为实际上线程是退出了的,但是我就是纠结为什么MsgWaitForMultipleObjects等不到线程退出,它不能返回WAIT_OBJECT_0

下面是DEBUG回显的值


example2.rar

29.41 KB, 下载次数: 0

实验程序源代码

回复 支持 反对

使用道具 举报

287

主题

668

帖子

5636

积分

学生管理组

Rank: 8Rank: 8

积分
5636
板凳
 楼主| 发表于 2015-6-16 23:59:40 | 只看该作者
这个时候,已经是弄得我晕头转向的了,因为不正确的进程退出是不对的,后果是不可估计的,可能对生产设备造成损伤,到时候追责就麻烦了。。。

为了解决这个问题,我继续研究了下,有了一系列的追踪调试,把代码改成下面那样,加了些trace进去:
[C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode
void CExample2Dlg::OnButton1()	//用按钮开启线程的函数
{
	// TODO: Add your control notification handler code here3
	DWORD result ;
    MSG msg ; 
	if(runflag==1)		//如果线程正在运行
	{
		SetEvent(m_CANEvent);	//发出让线程结束
		TRACE("发送SetEvent信号,让线程结束 \n");
		while (TRUE)  
		{  
			//wait for m_hThread to be over,and wait for  
			//QS_ALLINPUT(Any message is in the queue)  
			DWORD dwRet = MsgWaitForMultipleObjects (1, m_CANSendThreadHandle,FALSE, INFINITE,  QS_ALLINPUT);  
				 
			if(result == WAIT_OBJECT_0)   
			{
					TRACE("MsgWaitForMultipleObjects 返回值 %ld 从WAIT_OBJECT_0消息退出while循环 \n",dwRet);
					break; //break the loop 
			}
			else if(result == WAIT_OBJECT_0 + 1)   
			{
					//get the message from Queue  
					//and dispatch it to specific window 
					TRACE("MsgWaitForMultipleObjects 返回值 %ld 说明返回WAIT_OBJECT_0 + 1 ,分发消息,继续while循环\n",dwRet);
					PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);  
					DispatchMessage(&msg);   
					continue;
			}
			else if( result == WAIT_ABANDONED_0 )
			{
					TRACE("MsgWaitForMultipleObjects 返回值 %ld 说明返回WAIT_ABANDONED_0 ,跳出while\n",dwRet);
					break;
			}
			else
			{
					TRACE("MsgWaitForMultipleObjects 返回值 %ld 从default退出循环 \n",dwRet);
					DWORD dErrCode=GetLastError();
					TRACE("dErrCode=%ld  \n",dErrCode);
					break; // unexpected failure 
			}
		}
	}
	ResetEvent(m_CANEvent);		//设置无信号,让线程进入等待超时的工作
	uiButton=1;				//标记这是按钮响应的函数启动的线程
	pCANThreads=AfxBeginThread(MyThread1,this);
	m_CANSendThreadHandle[0]=pCANThreads->m_hThread;
	runflag=1;
}


运行结果如图:

发现 MsgWaitForMultipleObjects居然可以返回1,不知道这个1是怎么来的。。用GetLastError();也看到 没有出现函数调用上的错误。不知道为什么。。。
回复 支持 反对

使用道具 举报

287

主题

668

帖子

5636

积分

学生管理组

Rank: 8Rank: 8

积分
5636
地板
 楼主| 发表于 2015-6-17 00:14:31 | 只看该作者
finally  I  fixed this problem。。。。费了我好多天时间,感觉脸上皱纹都多了几根,做梦都在想怎么解决这种问题,
终于解决了,明天终于回到学校了,在这种厂里面呆着,呆久了心里就莫名的烦躁,看着这些操作工做着周而复始,始终如一的重复动作,有时候我真的闪过一瞬间的念头,那就是也许我写的这些智能化程序把这些人害了。。。。

其实很简单  就是在创建线程的时候 用CreateThread才行  因为他返回的那个HANDLE才是有效的, 而我前面用的
[C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode
	pCANThreads=AfxBeginThread(MyThread1,this);
	m_CANSendThreadHandle[0]=pCANThreads->m_hThread;

这里的句柄是不对的  在程序运行的时候 DWORD dErrCode=GetLastError(); 有可能得到返回值为6  意思是无效的句柄

下面上正确的代码
[C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode
void CExample3Dlg::OnButton1() 
{
	// TODO: Add your control notification handler code here
	DWORD result ;
    MSG msg ; 
	if(runflag==1)		//如果线程正在运行
	{
		SetEvent(m_CANEvent);	//发出让线程结束
		TRACE("发送SetEvent信号,让线程结束 \n");
		while (TRUE)  
		{  
			//wait for m_hThread to be over,and wait for  
			//QS_ALLINPUT(Any message is in the queue)  
			result = MsgWaitForMultipleObjects (1, m_CANSendThreadHandle,FALSE, INFINITE,  QS_ALLINPUT);  
				 
			if(result == WAIT_OBJECT_0)   
			{
				TRACE("MsgWaitForMultipleObjects 返回值 %ld 从WAIT_OBJECT_0消息退出while循环 \n",result);
				break; //break the loop 
			}
			else if(result == WAIT_OBJECT_0 + 1)   
			{
					//get the message from Queue  
					//and dispatch it to specific window 
				TRACE("MsgWaitForMultipleObjects 返回值 %ld 说明返回WAIT_OBJECT_0 + 1 ,分发消息,继续while循环\n",result);
                while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
                {   
                    //afxDump << msg.message << "\n";
					TRACE("分发的消息ID=0x%x \n",msg.message);
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
				continue;
				/*
				PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);  
				DispatchMessage(&msg);   
				continue;
				*/
			}
			else if( result == WAIT_ABANDONED_0 )
			{
				TRACE("MsgWaitForMultipleObjects 返回值 %ld 说明返回WAIT_ABANDONED_0 ,跳出while\n",result);
				break;
			}
			else
			{
				TRACE("MsgWaitForMultipleObjects 返回值 %ld 从default退出循环 \n",result);
				DWORD dErrCode=GetLastError();
				TRACE("dErrCode=%ld  \n",dErrCode);
				break; // unexpected failure 
			}
		}
	}
	ResetEvent(m_CANEvent);		//设置无信号,让线程进入等待超时的工作
	uiButton=1;				//标记这是按钮响应的函数启动的线程
	m_CANSendThreadHandle[0]=CreateThread(NULL,0,MyThread3,this,0,NULL);
	runflag=1;	
}

DWORD WINAPI CExample3Dlg::MyThread3(LPVOID param)
{
	TRACE("MyThread1 Begin\n");
	CExample3Dlg *Dlg=(CExample3Dlg *)param;
	
	int i;
	CString tmp;
	DWORD dw;

	for(i=0;i<100000;i++)
	{
		dw = WaitForSingleObject(Dlg->m_CANEvent,10); //让其延迟10ms就输出一个数
		if( dw == WAIT_OBJECT_0 )	//收到结束线程信号
		{
			Dlg->runflag=0;		//线程运行标志复位
			TRACE("MyThread1 End 1\n");
			return 0;			//退出

		}
		if( dw == WAIT_TIMEOUT)		//延迟到了,就执行下面的显示数值
		{
			if(Dlg->uiButton == 1)
			{
				tmp.Format(" %d ",i);
				Dlg->m_count.SetWindowText(tmp);	//这个是让其在界面上面显示当前i的数值
			}
			if(Dlg->uiButton ==2 )
			{
				tmp.Format(" %d ",i);
				Dlg->m_count2.SetWindowText(tmp);	//这个是让其在界面上面显示当前i的数值
			}
		}
	}
	
	Dlg->runflag=0;		//线程运行标志复位
	TRACE("MyThread1 End\n");
	return 0;
}


给出调试的DEBUG截图


最后给出MSDN上面的解释


MSDN上面根本就没有说用AfxBeginThread  是我一厢情愿的用这个函数的,因为太多网友说 ,在MFC里面不要用CreateThread....然而,在这个例子里面,则必须用这个函数来创建线程

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|cqutlab ( 渝ICP备15004556号

GMT+8, 2024-4-30 02:19 , Processed in 0.237567 second(s), 30 queries .

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表