多重视图
窗口的动态拆分
可以“动态”增减窗口。但每个窗口都使用相同的View类,显示出来的东西千篇一律,甚至各个窗口并非完全独立,一些窗口公用滚动条。
AppWizard中,可以指定选择分割窗口。或者
在CChildFrame中,声明一个CSplitterWnd对象m_wndSplitter;然后在CChildFrame::OnCreateClient中调用m_wndSplitter.Create即可。
窗口的静态拆分
其优缺点刚好与动态拆分相反。
静态拆分实现最简单的方法是先用AppWizzard生成动态拆分代码,然后作一些修改。
只要把上面的m_wndSplitter.Create用一个m_wndSplitter.CteateStaticd和若干个m_wndSplitter.CteateView来代替即可。
CreateStatic参数中,需指明父窗口及横列和纵行的个数,窗口风格等。若把父窗口指定为分割窗口中的某个窗口,则可以创建出一些三叉拆分的窗口来。
CreateView可以对指定的窗口号中设定View。参数中,可以指明所采用的View类的CRuntimeClass指针。
同源子窗口
Window/New Window菜单点击后,程序会做出当前View窗口的另一份拷贝。当然,我们可以跟踪这部分代码,并做出修改,使得程序可以使用另一种View打开同一份文档。
事实上,这个消息最终被CMDIFrameWnd::OnWindowNew拦截。我们可以仿照该方法写一个类似的函数来实现该功能。
多重文件
以上都是用不同的方式在不同的窗口中显示出同一份文件数据。而此处支持多种文件类型。为此,我们得做出如下改动:
1.新的Document类;
2.新的Document Template。在MyWinApp::InitInstance中引入新的模板。该模板中必须指定新的Frame,新的UI资源等;
3.新的UI系统;
4.新文件的读写操作。
由于此时程序支持多个文件类型,故点击“new”菜单时,会谈出窗口提示new哪种文件类型。
第14章 MFC多线程程序设计
CreateThread可以产生一个线程,其本体就是该函数的第三个参数所指定的一个函数,我们称之为线程函数。
三个概念:
模块
一段可执行的程序(包括exe和dll),其程序代码,数据,资源被加载到内存中,由系统建置一个数据结构来管理它,就是一个模块。
进程
进程主要是用来描述一大堆拥有权的。进程拥有地址空间,动态配置而来的内存,文件,线程和一系列模块。
线程
线程主要是用来表达模块中的程序代码的执行事实。
当Windows加载器将程序加载到内存中时,Kernell32挖出一些内存,建构一个进程表项,一个线程表项,若干个模块表项(程序可以使用多个dll文件);对线程表项,操作系统会构造出页表,消息队列,handle表格,环境数据结构等数据结构;在这些都构造好后,程序计数器指向第一条语句。
线程优先级
0~31,数值越大,优先级越高。
最初进程是以一个线程(称为主线程)作为开始,必要时,进程可以产生更多的线程。在CreateProcess的参数中先设定好进程的优先级;然后在CreateThread参数中设定好相对进程优先级的微调优先级。
::SetThreadPriority可以微调线程的优先级(-2~+2)。
从程序设计层面看线程
多线程不会让程序跑得更快(除非是多CPU),只是让程序比较“有反应”
使用多线程的好时机是:若程序有许多事情要做,但你还要随时保持某些外部时间(如鼠标操作),这时候就需要多线程。
从MFC角度看,线程划分成两类:
1.worker threads:与使用者UI无关的
2.UI threads:和使用者UI有关
基本上,当我们以::CreateThread产生一个线程,并指定一个线程函数,它就是worker thread,除非在他的生命中接触到了输入消息(有一个消息循环),此时该线程就变成UI thread。
线程本来就带消息队列,如果线程代码中带有一个消息循环,就成为UI thread。
MFC多线程程序设计
如同CWinApp代表一个程序本身一样,CWinThread对象代表一个线程本身。我们在进入AfxWinMain前就生成了一个CWinApp全局对象,在生成这个对象过程中,也在收集线程的相关信息。(注意CWinApp派生自CWinThread)
当然,程序可以有多个CWinThread对象。当我们需要建立一个额外的线程时,我们不应该直接调用::CreateThread或_beginthreadex,而应该先产生一个CWinThread对象,再调用其成员函数
CreateThread或全局函数AfxBeginThread将线程产生出来。当然,这个成员函数或者全局函数内部调用了::CreateThread或_beginthreadex(事实上是后者).
MFC不直接调用::CreateThread或者_beginthreadex的原因是,CWinThread::CreateThread和AfxBeginThread不仅仅是对::CreateThread的一层封装,而是做了其他一些必要的内部数据初始化工作。
产生一个worker线程
由于不牵扯到UI,所以只需要准备一个线程函数,然后调用AFxBeginThread来获得一个线程。
CWinThread* pThread=AfxBeginThread(ThreadFunc,...);要注意线程函数是Callback函数,不允许有this指针,可以把它声明为static成员函数或者全局函数。
产生一个UI线程
UI线程不光要有一个线程函数,还应该有一个消息循环。而CWinThread::Run里就有一个ie消息循环,所以我们只需要从CWinThread里派生出自己的类,再调用AfxBeginThread产生一个eCWinThread对象。
线程的结束
worker线程的生命就是线程函数本身,所以return后,线程就结束了,当然也可以调用AfxEndThread结束一个线程。
UI线程有一个消息循环,所以必须在消息队列中放入一个WM_QUIT(调用:ostQuitMessage即可或者线程的任意一个函数中调用AfxEndThread),才能结束。
《深入浅出MFC》读书笔记(十五)(包括“应用程序”和“线程”的关系)
最新推荐文章于 2022-10-05 20:29:19 发布