【求助】MsgWaitForMultipleObjects 函数的问题

Squall001 2014-05-18 02:26:55
这个问题是来自我上一个提问的帖子 http://bbs.csdn.net/topics/390790125
上一个问题帖子的解决办法就是这个帖子的做法,但是又遇到新问题。。。

下面是我的代码, 然后再引出问题:
这是一个基于对话框的程序,
首先在头文件里面有几个变量的声明:

BOOL runflag; //这个为否正在发送数据的标志
int uiButton; //这个是表明哪一个函数启动的发送线程 的标志

HANDLE m_CANEvent;
HANDLE m_CANSendThreadHandle[3]; //这里定义3个handle 实际只用一个
CWinThread *pCANThreads;


下面是CPP里面的代码

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窗口截图;
...全文
394 39 打赏 收藏 转发到动态 举报
写回复
用AI写文章
39 条回复
切换为时间正序
请发表友善的回复…
发表回复
Extre_Lin 2015-01-17
  • 打赏
  • 举报
回复
你好,也是关于MsgWaitForMultipleObjects 这个函数的,想请教你,我QQ:421329605.麻烦你了
Squall001 2014-05-30
  • 打赏
  • 举报
回复
引用 37 楼 schlafenhamster 的回复:
http://www.cnblogs.com/lancidie/archive/2011/04/06/2006748.html
其实这个帖子我已经看过了 它也是用的CreateThread来创建进程,如果用AfxBeginThread的话 随便怎么都是错的。
schlafenhamster 2014-05-19
  • 打赏
  • 举报
回复
而且就从来没经过: // else if( result == WAIT_ABANDONED_0 ) // {// ABANDONED // afxDump << msg.message << " abandoned\n"; // break; // } // else // {// error // afxDump << msg.message << " error\n"; // DWORD dErrCode=GetLastError(); // break; // }
schlafenhamster 2014-05-19
  • 打赏
  • 举报
回复
参考32楼 TRUE 和 FALSE 的 算法 不一样 现在是 FALSE, evevnt 返回是 1 ?. 如果 TRUE 返回是 0. ?
schlafenhamster 2014-05-18
  • 打赏
  • 举报
回复
1. 512 =0x0200 #define WM_MOUSEMOVE 0x0200 2. result 没用了! while (result=PeekMessage(&msg,NULL,0,0,PM_REMOVE)) 不用的消息能不能 不要 ? 那2个 0,0
Squall001 2014-05-18
  • 打赏
  • 举报
回复
引用 4 楼 schlafenhamster 的回复:
就是 PeekMessage 返回值 != 0 ,但 =-1 ??
以下是我源程序的下载地址 http://pan.baidu.com/s/1gdHwkZx 我做了以下追踪改动
			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里面调试信息是 :
收到消息,函数返回值为1,进入消息分发 
message里面消息值 512
schlafenhamster 2014-05-18
  • 打赏
  • 举报
回复
就是 PeekMessage 返回值 != 0 ,但 =-1 ??
schlafenhamster 2014-05-18
  • 打赏
  • 举报
回复
“一直在while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))那里循环了,” 就是 PeekMessage 返回值〉0 afxDump << msg.message << "\n"; 看看他取到了什么?
Squall001 2014-05-18
  • 打赏
  • 举报
回复
引用 1 楼 schlafenhamster 的回复:
PeekMessage(&msg,NULL,0,0,PM_REMOVE) 试试 PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)
很高兴你能回复 不过我试了,如果按照你的方法,消息如果不被取出队列,那么再次点击那个按钮就失败全部死机了。
schlafenhamster 2014-05-18
  • 打赏
  • 举报
回复
PeekMessage(&msg,NULL,0,0,PM_REMOVE) 试试 PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)
Squall001 2014-05-18
  • 打赏
  • 举报
回复
最后给出MSDN上面的解释




MSDN上面根本就没有说用AfxBeginThread 是我一厢情愿的用这个函数的,因为太多的帖子说 ,在MFC里面不要用CreateThread.....
Squall001 2014-05-18
  • 打赏
  • 举报
回复
引用 31 楼 guyuguang8628391 的回复:
MsgWaitForMultipleObjects 这种api尽量少用,用WaitForMultipleObjects


我觉得 还是不要去怀疑这个函数有问题吧

我解决了这个问题

其实很简单 就是在创建线程的时候 用CreateThread才行 因为他返回的那个HANDLE才是有效的, 而我前面用的

pCANThreads=AfxBeginThread(MyThread1,this);
m_CANSendThreadHandle[0]=pCANThreads->m_hThread;

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

下面上正确的代码


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截图

schlafenhamster 2014-05-18
  • 打赏
  • 举报
回复
result=MsgWaitForMultipleObjects(1,m_CANSendThreadHandle,TRUE,INFINITE, QS_ALLINPUT | QS_ALLEVENTS | QS_ALLPOSTMESSAGE | QS_SENDMESSAGE); if(result == WAIT_OBJECT_0) {// event afxDump << result << " =WAIT_OBJECT_0\n"; break; } TRUE 和 FALSE 的 算法 不一样.
图灵转世 2014-05-18
  • 打赏
  • 举报
回复
MsgWaitForMultipleObjects 这种api尽量少用,用WaitForMultipleObjects
Squall001 2014-05-18
  • 打赏
  • 举报
回复
引用 29 楼 schlafenhamster 的回复:
17楼的 "开启线程" 有时工作 , 有时不工作 (计数 停)
对啊 就是因为 退出那个while循环根本就不是从result == WAIT_OBJECT_0 这里退出的 所以产生的后果无法预料 线程退出 和开启都要一点时间的 你可以点快点 多点几次 button1 就可以看到实际上开启了多个线程
schlafenhamster 2014-05-18
  • 打赏
  • 举报
回复
17楼的 "开启线程" 有时工作 , 有时不工作 (计数 停)
Squall001 2014-05-18
  • 打赏
  • 举报
回复
引用 27 楼 schlafenhamster 的回复:
"开启线程" 按一下, 老线程关闭,新线程开始(重头计数) 计数是不会停的.
对啊 只要开启了线程 计数器永远就不会停 只会从头计数 你是不是 点快了 计数器就停止了?
schlafenhamster 2014-05-18
  • 打赏
  • 举报
回复
"开启线程" 按一下, 老线程关闭,新线程开始(重头计数) 计数是不会停的.
Squall001 2014-05-18
  • 打赏
  • 举报
回复
引用 25 楼 schlafenhamster 的回复:
"开启线程" 有时工作 , 有时不工作 (计数 停)
什么意思?
加载更多回复(19)
API之网络函数1. API之网络函数 WNetAddConnection 创建同一个网络资源的永久性连接 WNetAddConnection2 创建同一个网络资源的连接 WNetAddConnection3 创建同一个网络资源的连接 WNetCancelConnection 结束一个网络连接 WNetCancelConnection2 结束一个网络连接 WNetCloseEnum 结束一次枚举操作 WNetConnectionDialog 启动一个标准对话框,以便建立同网络资源的连接 WNetDisconnectDialog 启动一个标准对话框,以便断开同网络资源的连接 WNetEnumResource 枚举网络资源 WNetGetConnection 获取本地或已连接的一个资源的网络名称 WNetGetLastError 获取网络错误的扩展错误信息 WNetGetUniversalName 获取网络中一个文件的远程名称以及/或者UNC(统一命名规范)名称 WNetGetUser 获取一个网络资源用以连接的名字 WNetOpenEnum 启动对网络资源进行枚举的过程 2. API之消息函数 BroadcastSystemMessage 将一条系统消息广播给系统中所有的顶级窗口 GetMessagePos 取得消息队列中上一条消息处理完毕时的鼠标指针屏幕位置 GetMessageTime 取得消息队列中上一条消息处理完毕时的时间 PostMessage 将一条消息投递到指定窗口的消息队列 PostThreadMessage 将一条消息投递给应用程序 RegisterWindowMessage 获取分配给一个字串标识符的消息编号 ReplyMessage 答复一个消息 SendMessage 调用一个窗口的窗口函数,将一条消息发给那个窗口 SendMessageCallback 将一条消息发给窗口 SendMessageTimeout 向窗口发送一条消息 SendNotifyMessage 向窗口发送一条消息 3. API之文件处理函数 CloseHandle 关闭一个内核对象。其中包括文件、文件映射、进程、线程、安全和同步对象等 CompareFileTime 对比两个文件的时间 CopyFile 复制文件 CreateDirectory 创建一个新目录 CreateFile 打开和创建文件、管道、邮槽、通信服务、设备以及控制台 CreateFileMapping 创建一个新的文件映射对象 DeleteFile 删除指定文件 DeviceIoControl 对设备执行指定的操作 DosDateTimeToFileTime 将DOS日期和时间值转换成一个 win32 FILETIME 值 FileTimeToDosDateTime 将一个 win32 FILETIME 值转换成DOS日期和时间值 FileTimeToLocalFileTime 将一个FILETIME结构转换成本地时间 FileTimeToSystemTime 根据一个FILETIME结构的内容,装载一个SYSTEMTIME结构 FindClose 关闭由FindFirstFile函数创建的一个搜索句柄 FindFirstFile 根据文件名查找文件 FindNextFile 根据调用FindFirstFile函数时指定的一个文件名查找下一个文件 FlushFileBuffers 针对指定的文件句柄,刷新内部文件缓冲区 FlushViewOfFile 将写入文件映射缓冲区的所有数据都刷新到磁盘 GetBinaryType 判断文件是否可以执行 GetCompressedFileSize 判断一个压缩文件在磁盘上实际占据的字节数 GetCurrentDirectory 在一个缓冲区中装载当前目录 GetDiskFreeSpace 获取与一个磁盘的组织有关的信息,以及了解剩余空间的容量 GetDiskFreeSpaceEx 获取与一个磁盘的组织以及剩余空间容量有关的信息 GetDriveType 判断一个磁盘驱动器的类型 GetExpandedName 取得一个压缩文件的全名 GetFileAttributes 判断指定文件的属性 GetFileInformationByHandle 这个函数提供了获取文件信息的一种机制 GetFileSize 判断文件长度 GetFileTime 取得指定文件的时间信息 GetFileType 在给出文件句柄的前提下,判断文件类型 GetFileVersionInfo 从支持版本标记的一个模块里获取文件版本信息

16,472

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

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