1.前言
前面几篇文章着重介绍了多线程的三种创建方式及多线程间的4种通信方式,并采用大量的实例演示,相信大家对线程的创建和使用有了一定的了解。若还不了解请复习下前面的文章,多动手写代码和调试,光看不练,假把式。
今天先请大家看看下面一个多线程程序,操作很简单,就是创建9个线程,并输出相应的线程编号(即报数)。主要代码如下:
[cpp]
//声明线程处理函数
unsigned __stdcallThreadFunc( void* pArguments);//工作线程函数
HANDLE m_handle[9];//线程句柄列表
CListBox m_List; //数据列表控件
/////////////////////////////////////////////////
int g_nCount= 0;//这个是全局变量,用于线程报数(计数)
//演示开始:创建线程
void CThreadProblem1Dlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
GetDlgItem(
IDC_BUTTON1)->EnableWindow(FALSE);
m_List.ResetContent();//清空列表
g_nCount = 0; //重置报数,
SetDlgItemInt(IDC_EDIT_NUM,++m_nNum); //显示操作的次数
//创建多线程
for (int i=0;i<9;i++)
{
m_handle[i] = (HANDLE)_beginthreadex(NULL,0, ThreadFunc,&m_List,0, NULL);
}
//WaitForMultipleObjects(10, handle, TRUE, INFINITE); //在此处等待退出,将发现程序假死了。所以采用线程的方式等待
_beginthreadex(NULL,0, WaitThread,this, 0, NULL); //等待上述的个线程都退出
}
//工作线程函数
unsigned __stdcall ThreadFunc(void* pArguments)
{
Sleep(100);//相关处理
g_nCount++; //计数加
CListBox *pList= (CListBox*)pArguments;
CString str;
str.Format(" 子线程ID号为%4d 报数为:%d",GetCurrentThreadId(),g_nCount);
pList->AddString(str);//输出
Sleep(100);//相关处理
return 0;
}
//线程函数:等待个演示线程都退出再使能开始按钮
unsigned __stdcall WaitThread(void* pArguments)
{
CThreadProblem1Dlg *pMainDlg= (CThreadProblem1Dlg *)pArguments;
WaitForMultipleObjects(9,pMainDlg->m_handle,TRUE, INFINITE); //等待所有线程都结束
EnableWindow(GetDlgItem(AfxGetApp()->m_pMainWnd->m_hWnd,IDC_BUTTON1),TRUE);//使能开始按钮
return 0;
}
当运行一次OnBnClickedButton1()函数,将显示下面的结果:
你一看,没错呀,就应该是这样的,没有错呀!多运行几次也是这样的。但我要肯定的告诉你,上面的程序是有严重的问题,而运行结果也欺骗了你。正是运行结果大大蒙骗了你的理智和大脑。你发现问题了吗?(提示:不是报数顺序的问题)www.2cto.com
正是该错误有隐蔽性,你很难从结果中发现问题,除非你运气特别好,一运行就能重现问题,但作为程序员,你决不能仅靠运气,不可能你每次的运气都这么好。
多运行几次上面的程序,你有可能发现问题,现在我把该程序改进下,使其具有自动识别错误的智能,你一眼就能发现问题的。
改进点:添加结果检测功能,若正常,其线程的报数应该为1-9,有可能顺序有变,但总和为45=1+2+3+4+5+6+7+8+9。程序将一直循环到程序退出。若不等于45就退出循环,表示有问题,即让程序一直运行,直到有错误为止。前面的OnBnClickedButton1()函数和ThreadFunc()函数保存不变,WaitThread()函数添加一个判断语句,改进程序如下:
[cpp]
//线程函数:等待个演示线程都退出再使能开始按钮
unsigned __stdcall WaitThread(void* pArguments)
{
CThreadProblem1Dlg *pMainDlg= (CThreadProblem1Dlg *)pArguments;
WaitForMultipleObjects(9, pMainDlg->m_handle, TRUE,INFINITE); //等待所有线程都结束
EnableWindow(GetDlgItem(AfxGetApp()->m_pMainWnd->m_hWnd,IDC_BUTTON1),TRUE);//使能开始按钮
if(pMainDlg->m_bAuto&& !pMainDlg->IsError())
{//若自动使能,则继续下轮操作
pMainDlg->OnBnClickedButton1();
}
return 0;
}
//添加IsError()函数,用以判断结果是否正确。
// 自动判断每次运行结果是否正确
bool CThreadProblem1Dlg::IsError(void)
{
int nValue[9]={0};
int nResult = 0;
CString szText;
for(int i=0;i<9;i++)
{//得到各个线程的报数
m_List.GetText(i,szText);
szText = szText.Right(1);
nValue[i] = atoi(szText);
nResult += nValue[i];
}
//判断是否有相同的值出现
if (nResult != 45)
{//有错误
return t