这个是我写的演示程序的主界面,左边使用图形显示哲学家就餐中的一些状态切换,右边用编辑框给每位哲学家配以文字说明。
一、先说下演示程序的核心部分,CWinThread类和CCriticalSection临界区类
MFC给我们封装好了线程类CWinThread供我们在程序里面去创建新的线程,其提供了两种线程,工作线程和用户线程(具体区别可以去查相应资料,这里就不多说了),本程序采用的是工作线程(工作线程就是在后台处理大量数据,并不需要响应消息请求,用户线程就是在线程运行的时候还要时刻捕获用户的消息请求)。
工作线程创建很简单
CWinThread *pThread;
pThread->AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOIDpParam, int nPriority = THREAD_PRIORITY_NORMAL, UINTnStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTESlpSecurityAttrs = NULL );
函数具体参数可以查MSDN,我们常用的就是前面两个参数,第一个是是指定我们创建的线程所执行的代码,第二个参数是我们要传递给线程的数据结构。
本程序因为有六位哲学家,所以我创建了6个线程,并为其指定了执行函数
CWinThread *pthread[6]; for(int i=0;i<6;i++) pthread[i]=AfxBeginThread(Eat,&imageinfo[i]);其中Eat是我指定线程执行的代码,因为本演示程序中六位哲学家做的事都是一样的,所以每位哲学家线程的执行代码也是一样的,imageinfo是我要从主程序中传递给线程的一些基本数据信息。
struct ImageInfo { int index; //索引,表示当前的是几号哲学家线程在执行 CPoint pt_chair; //椅子的坐标 CPoint pt_chopstick_middle[2]; // CPoint pt_chopstick_start[2]; //叉子的坐标信息 CPoint pt_chopstick_end[2]; // double radius_chair; //椅子半径 double radius_table; //桌子的半径 CWnd* pWnd; //主程序窗口的句柄 CRect rect; HWND edit; //右边相应编辑框的句柄 };
UINT Eat(LPVOID param) { while (true) //设置为无线循环,好让线程一直运行下去 { ImageInfo *imageinfo=(ImageInfo*)param; CDC *pDC=imageinfo->pWnd->GetDC(); //获得主程序窗口的DC //char textbuff[1024]; //::GetWindowText(imageinfo->edit,textbuff,1024); CString text; CString temp; bool caneat=FALSE; //标识变量,验证当前哲学家线程是否可以就餐
CBrush brush_think,brush_eat,brush_wait,*oldbrush; //表示各种状态的画刷和画笔
CPen whitepen,blackpen,backpen,*oldpen; whitepen.CreatePen(PS_SOLID,4,RGB(255,255,255)); blackpen.CreatePen(PS_SOLID,4,RGB(0,0,0)); backpen.CreatePen(PS_SOLID,4,RGB(240,240,240)); brush_think.CreateSolidBrush(RGB(255,0,0)); brush_eat.CreateSolidBrush(RGB(0,255,0)); brush_wait.CreateSolidBrush(RGB(0,0,255)); CRect rect; //思考阶段 temp.Format("开始思考!"); //text.Format(textbuff); //text=text+"\r\n"+temp; ::SetWindowText(imageinfo->edit,(LPCTSTR)temp); oldbrush=(CBrush*)pDC->SelectObject(&brush_think); rect.left=imageinfo->pt_chair.x-imageinfo->radius_chair; rect.right=imageinfo->pt_chair.x+imageinfo->radius_chair; rect.top=imageinfo->pt_chair.y-imageinfo->radius_chair; rect.bottom=imageinfo->pt_chair.y+imageinfo->radius_chair; pDC->Ellipse(rect); //num.Format("%d",imageinfo->index+1); //pDC->DrawText(num,rect,DT_CENTER|DT_VCENTER|DT_SINGLELINE); pDC->SelectObject(oldbrush); g_cls.Lock(); //g_cls为临界区变量,此处进入临界区 if (chopsticks[imageinfo->index]==1&&chopsticks[(imageinfo->index+5)%6]==1) { chopsticks[imageinfo->index]=0; chopsticks[(imageinfo->index+5)%6]=0; caneat=TRUE; } g_cls.Unlock(); //退出临界区 Sleep(2000); if (FALSE==caneat) { temp.Format("没有拿到两个叉子,无法吃饭!"); ::SetWindowText(imageinfo->edit,(LPCTSTR)temp); Sleep(1000); temp.Format("开始等待..."); ::SetWindowText(imageinfo->edit,(LPCTSTR)temp); oldbrush=(CBrush*)pDC->SelectObject(&brush_wait); pDC->Ellipse(rect); pDC->SelectObject(oldbrush); //pDC->DrawText(num,rect,DT_CENTER|DT_SINGLELINE|DT_VCENTER); Sleep(2000); } if (TRUE==caneat) { temp.Format("成功拿到两个叉子,开始吃饭!"); ::SetWindowText(imageinfo->edit,(LPCTSTR)temp); Sleep(1000); temp.Format("吃饭...",imageinfo->index); //::GetWindowText(imageinfo->edit,textbuff,1024); //text.Format(textbuff); //text=text+"\r\n"+temp; ::SetWindowText(imageinfo->edit,(LPCTSTR)temp); //吃饭的状态,拿起筷子 oldpen=(CPen*)pDC->SelectObject(&blackpen); pDC->MoveTo(imageinfo->pt_chair); pDC->LineTo(imageinfo->pt_chopstick_middle[0]); pDC->MoveTo(imageinfo->pt_chair); pDC->LineTo(imageinfo->pt_chopstick_middle[1]); pDC->SelectObject(oldpen); oldpen=(CPen*)pDC->SelectObject(&whitepen); pDC->MoveTo(imageinfo->pt_chopstick_middle[0]); pDC->LineTo(imageinfo->pt_chopstick_end[0]); pDC->MoveTo(imageinfo->pt_chopstick_middle[1]); pDC->LineTo(imageinfo->pt_chopstick_end[1]); pDC->SelectObject(oldpen); oldbrush=(CBrush*)pDC->SelectObject(&brush_eat); pDC->Ellipse(rect); pDC->SelectObject(oldbrush); //pDC->DrawText(num,rect,DT_VCENTER|DT_CENTER|DT_SINGLELINE); Sleep((imageinfo->index+1)*1000); //吃完饭,放下筷子 oldpen=(CPen*)pDC->SelectObject(&blackpen); pDC->MoveTo(imageinfo->pt_chopstick_middle[0]); pDC->LineTo(imageinfo->pt_chopstick_end[0]); pDC->MoveTo(imageinfo->pt_chopstick_middle[1]); pDC->LineTo(imageinfo->pt_chopstick_end[1]); pDC->SelectObject(oldpen); oldpen=(CPen*)pDC->SelectObject(&backpen); pDC->MoveTo(imageinfo->pt_chair); pDC->LineTo(imageinfo->pt_chopstick_middle[0]); pDC->MoveTo(imageinfo->pt_chair); pDC->LineTo(imageinfo->pt_chopstick_middle[1]); pDC->SelectObject(oldpen); oldbrush=(CBrush*)pDC->SelectObject(&brush_eat); pDC->Ellipse(rect); pDC->SelectObject(oldbrush); g_cls.Lock(); chopsticks[imageinfo->index]=1; chopsticks[(imageinfo->index+5)%6]=1; g_cls.Unlock(); temp.Format("吃完饭,放下叉子!"); ::SetWindowText(imageinfo->edit,(LPCTSTR)temp); Sleep(1000); caneat=FALSE; } } //::SetWindowText(imageinfo->edit,"1"); return TRUE; }线程处理函数里面主要的就是一个临界区变量g_cls,其是MFC提供的一个同步互斥类CCriticalSection的一个对象,其管理控制线程对共享资源的访问,MFC总共提供了四种同步互斥类可以用以处理共享变量的问题(CMutex, CSemaphore ,CEvent,CCriticalSection,各有区别,具体可以去网上查找相关资料或者看MSDN )。
临界区对象要和共享变量一起放在MFC框架代码的外面
//全局变量 CCriticalSection g_cls;//临界区对象 int chopsticks[6];在g_cls.Lock()和g_cls.Unlock()之间的变量将限制为同一时刻只能让一位线程访问。
二、演示程序的图形绘制部分
这部分主要就是计算坐标有点麻烦,绘图用CDC自带的一些绘图函数绘制而成。
void CPhilosophy_EatingDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { CDialog::OnPaint(); } //获得显示框的大小rect CRect rect; GetDlgItem(IDC_STATIC_TABLE)->GetWindowRect(&rect); ScreenToClient(&rect); /***************************************************************/ //计算这个rect的中心坐标,求出桌子中心的坐标,以及其外接矩形的坐标// /***************************************************************/ //1.计算桌子中心的坐标 pt_table.x=(rect.right+rect.left)/2; pt_table.y=(rect.bottom+rect.top)/2; //2.计算桌子外接矩形的大小 radius_table=(rect.right-rect.left)/3;//我们去显示框长度的1/3做为桌子的半径 rect.left=pt_table.x-radius_table; rect.right=pt_table.x+radius_table; rect.top=pt_table.y-radius_table; rect.bottom=pt_table.y+radius_table; CDC *pDC=GetDC(); pDC->Ellipse(rect); //计算每个椅子的中心坐标 double pi=3.1415926535; double sin_temp,cos_temp; double x=pi/3; for (int i=0;i<6;i++) { sin_temp=sin(i*x); cos_temp=cos(i*x); pt_chair[i].x=pt_table.x-(3*radius_table)/2.0*sin_temp; pt_chair[i].y=pt_table.y-(3*radius_table)/2.0*cos_temp; } radius_chair=radius_table/4.0; //计算每个椅子的外接矩形,并画出椅子 for (i=0;i<6;i++) { rect.left=pt_chair[i].x-radius_chair; rect.right=pt_chair[i].x+radius_chair; rect.top=pt_chair[i].y-radius_chair; rect.bottom=pt_chair[i].y+radius_chair; pDC->Ellipse(rect); CString num; num.Format("%d",i+1); pDC->DrawText(num,&rect,DT_CENTER|DT_VCENTER|DT_SINGLELINE);//单行居中 } //计算叉子的起始坐标和终止 for (i=0;i<6;i++) { pt_chopstick_start[i].x=pt_table.x-radius_table*sin(i*x+pi/6); pt_chopstick_start[i].y=pt_table.y-radius_table*cos(i*x+pi/6); pt_chopstick_end[i].x=pt_table.x-25*sin(i*x+pi/6); pt_chopstick_end[i].y=pt_table.y-25*cos(i*x+pi/6); } //画筷子 CPen pen(PS_SOLID,4,RGB(0,0,0)); pDC->SelectObject(&pen); for (i=0;i<6;i++) { pDC->MoveTo(pt_chopstick_start[i]); pDC->LineTo(pt_chopstick_end[i]); } }上述代码中的相关变量在CPhilosophy_EatingDlg中声明:
class CPhilosophy_EatingDlg : public CDialog { // Construction public: CPhilosophy_EatingDlg(CWnd* pParent = NULL); // standard constructor public: ImageInfo imageinfo[6]; CWinThread *pthread[6]; CPoint pt_table; CPoint pt_chair[6]; double radius_table; double radius_chair; CPoint pt_chopstick_start[6]; CPoint pt_chopstick_end[6]; //CPoint pt_chopstick_start[6]; BOOL m_continue;
进程运行时图形变化的绘制已经在进程函数Eat中了,大家可以自己看下,不是很复杂。
三、两个按钮的代码
开始和停止按钮的代码,没什么内容,我把代码贴下,大家可以参考~
void CPhilosophy_EatingDlg::OnButtonStart() { // TODO: Add your control notification handler code here for (int m=0;m<6;m++) { chopsticks[m]=1; } for(int i=0;i<6;i++) { switch(i) { case 0: imageinfo[i].edit=GetDlgItem(IDC_EDIT_STATE1)->m_hWnd; break; case 1: imageinfo[i].edit=GetDlgItem(IDC_EDIT_STATE2)->m_hWnd; break; case 2: imageinfo[i].edit=GetDlgItem(IDC_EDIT_STATE3)->m_hWnd; break; case 3: imageinfo[i].edit=GetDlgItem(IDC_EDIT_STATE4)->m_hWnd; break; case 4: imageinfo[i].edit=GetDlgItem(IDC_EDIT_STATE5)->m_hWnd; break; case 5: imageinfo[i].edit=GetDlgItem(IDC_EDIT_STATE6)->m_hWnd; break; default: break; } imageinfo[i].pWnd=this; imageinfo[i].index=i; imageinfo[i].pt_chair=pt_chair[i]; imageinfo[i].pt_chopstick_middle[0]=pt_chopstick_start[i]; imageinfo[i].pt_chopstick_middle[1]=pt_chopstick_start[(i+5)%6]; imageinfo[i].radius_chair=radius_chair; imageinfo[i].radius_table=radius_table; imageinfo[i].pt_chopstick_end[0]=pt_chopstick_end[i]; imageinfo[i].pt_chopstick_end[1]=pt_chopstick_end[(i+5)%6]; pthread[i]=AfxBeginThread(Eat,&imageinfo[i]); } } void CPhilosophy_EatingDlg::OnButtonStop() { // TODO: Add your control notification handler code here if (FALSE==m_continue) { GetDlgItem(IDC_BUTTON_STOP)->SetWindowText("继续演示"); for (int i=0;i<6;i++) { pthread[i]->SuspendThread(); } m_continue=TRUE; return; } else { GetDlgItem(IDC_BUTTON_STOP)->SetWindowText("暂停演示"); for (int i=0;i<6;i++) { pthread[i]->ResumeThread(); } m_continue=FALSE; return; } }主要的就是这么多了,大家结合着代码看看,有不明白的欢迎和我交流~~~
贴几个程序运行的图片
源代码下载:http://download.csdn.net/detail/txg703003659/4069141!
我把代码贴出来是希望能帮助一些人,如果有不明白的可以和我讨论,希望不要将本程序直接拷贝,谢了!