《深入浅出MFC》读书笔记(十五)(包括“应用程序”和“线程”的关系)

多重视图

窗口的动态拆分
可以“动态”增减窗口。但每个窗口都使用相同的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),才能结束。

你可能感兴趣的:(多线程,数据结构,UI,读书,mfc,callback)