首页 > C/C++开发工具专区 > VC技术 > vc多线程编程
2006
09-04

在网上看到了好多关于多线程编程的问题,可是发现给出编程例子很简单;因为我要写一个服务程序,涉及到线程文档操作和视图类操作,找了好久也没有找到这样的例子(不好意思,我是一个初学者对vc不熟),通过查找一些资料但是这些资料上也只是说在线程操作文档类和视图类要注意的一些事情,没有具体的例子;我费了好长时间才知道怎么在线程中操作视图类和文档类(大家不要笑话,我太笨了)。


我将在线程中操作文档类和视图类总结如下(我是菜鸟有写错的地方希望高手给指出来以免误导他人):


1)具体怎样启动一个线程我就不用多说了,大家搜索一些资料,看看就可以了;


2)由于某个进程中运行的线程共享相同的内存空间,通常可以共享全局的或动态分配的变量和对象,即在新创建的线程中可以使用别的线程创建的变量和类对象(特别是主线程创建的变量和类对象)。但是,并不是可以在新线程中共享一切主线程的对象,在使用MFC对象时有两个限制:


1、两个线程不能同时访问同一MFC对象。例如:在系统中两个线程可以共享一个CLine对象,但不能同时调用CLine的成员函数。对于这个限制,可以进行解决,采用同步化线程的方法可以防止不同线程同时访问一个MFC对象。


2、有些MFC类以其派生类的对象只能由生成该对象的线程访问,这些类包括以下几种:


CWnd、CDC、CGdiObject 由这些类及派生类产生的对象,只能由生成该对象的线程来使用,别的线程不能使用。作为一个通用规则,线程只能访问它创建的MFC对象,这是因为临时的和永久的windows句柄映射保存于县城本地存贮中,这些句柄可以在多个县城访问数据时进行同步机制的保护。


解决这个问题有两个办法:(1)传送进一个单独的句柄,而不是一个C++对象进工作线程。工作线程通过调用适当的FromHandle函数将这些对象加载到它的临时映射中去。当然也可以通过调用Attach将对象加到永久映射中去,但是这时你必须能够保证这个对象的生存时间比线程要长。


(2)创建新的自定义消息,不同的线程使用不同的消息通知主程序什么事情发生了。



 



视图类CView是由CWnd类派生出来的,而MFC应用程序的视图类是由CView派生的,所以MFC程序框架中的视图类不能在新线程中使用。


例如:生成一个draw工程在CDrawview.cpp中定义


CDrawView *p_view;


然后使用线程函数


UINT threaddraw(LPVOID param)


{p_view->OnDraw(PDC);


return 0;


}


这种调用是不允许的,系统运行时会出现致命错误,因为在线程中使用了主线程中建立的CWnd派生对象。



 



对于上述的情况对于文档类来说是可以的,因为文档类CDocument不是以上四个类的派生类,可以在线程中使用在主线程中建立的对象。在文档类CDrawDoc中有一个用来完成视图绘制工作的函数。


UNIT threaddraw(LPVOID param)


{


       p_doc->Draw(pDc,0,0,0);


       return 0;


}


p_doc在文件drawview.cpp中定义成全局对象指针;


CDrawDoc *p_Doc;


在OnDraw函数中,在创建线程之前得到当前文档类对象的指针,对指针进行初始化;


void CDrawview::OnDraw(CDC *PDC)


{


 p_doc =GetDocument();


}


(以上内容参考vc++高级编程技术)


根据以上提供的内容我写了一个例子程序,该程序主要是在线程中访问文档类的变量,将文档类的变量修改后,结束子线程,子线程通过发送消息通知视图类更新视图显示出文档变量的内容。


首先生成一个单文档的应用,在菜单中新建一个菜单项,caption :start id IDD_THREADSTART


使用ctrl+w生成菜单命令函数如下:


void CTESTDRAWView::OnThreadstart()


{


       // TODO: Add your command handler code here


       p_doc=GetDocument();


       HWND hwnd =GetSafeHwnd();


       AfxBeginThread(threadproc,(LPVOID)hwnd,0,0,NULL);



 



}


在CTESTDRAWView.cpp中定义全局的CTESTDRAWDoc 文档变量如下:


CTESTDRAWDoc *p_doc;


在CTESTDRAWView.h文件的中CTESTDRAWView类声明前定义自定义消息如下:


const WM_THREADEND=WM_USER+100;


在CTESTDRAWView.h文件的中//}}AFX_MSG后添加消息相应函数


       afx_msg LONG OnThreadEnd();


在CTESTDRAWView.cpp中的   //}}AFX_MSG_MAP


       // Standard printing commands后添加消息处理对应函数如下:


       ON_MESSAGE(WM_THREADEND,OnThreadEnd)



 



编写函数如下:


LONG CTESTDRAWView::OnThreadEnd()


{


       CTESTDRAWDoc *pdoc=GetDocument();


       pdoc->UpdateAllViews(NULL);


       return 0;


}


该函数的主要功能实现在线程结束后更新视图类;


为视图类添加wm_paint消息处理函数


函数内容如下:


void CTESTDRAWView::OnPaint()


{


       CPaintDC dc(this); // device context for painting


       CTESTDRAWDoc *pdoc=GetDocument();


       dc.TextOut(10,10,pdoc->m_stri);


       // TODO: Add your message handler code here


      


       // Do not call CView::OnPaint() for painting messages


}


该函数主要功能是wm_paint相应消息更新视图将文档变量内容显示;


实际线程函数如下定义在CTESTDRAWView.cpp文件中


UINT threadproc(LPVOID param)


{


       p_doc->m_stri=”ok!!!!”;


       ::PostMessage((HWND)param,WM_THREADEND,0,0);



 



       return 0;


}


最后在CTESTDRAWDoc类中添加CString  m_str变量为public的


运行后使用菜单中start项,执行成功后再视图中出现ok!!!


如果要在线程中直接使用视图类显示内容可以按以下方式使用,在postmessage后添加


       CClientDC dc(CWnd::FromHandle((HWND)param));


       dc.TextOut(10,40,”hahahahaha”);


即可,为什么要放在postmessage后边我想大家比我还要清楚吧(^_^)。


留下一个回复