友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
深入浅出MFC第2版(PDF格式)-第137部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
已结束)
GetPriorityClass 取得某一进程的优先权等级
GetQueueStatus 传回某一执行线程的消息队列状态
GetThreadContext 取得某一执行线程的context
GetThreadDesktop 取得某一执行线程的desktop 对象
GetThreadPriority 取得某一执行线程的优先权
GetThreadSelectorEntry 除错器专用,传回指定之执行线程的某个selector 的
LDT 记录项
ResumeThread 将某个冻结的执行线程恢复执行
SetPriorityClass 设定优先权等级
SetThreadPriority 设定执行线程的优先权
Sleep 将某个执行线程暂时冻结。其它执行线程将获得执行权。
SuspendThread 冻结某个执行线程
TerminateThread 结束某个执行线程
TlsAlloc 配置一个TLS (Thread Local Storage )
TlsFree 释放一个TLS (Thread Local Storage )
TlsGetValue 取得某个TLS (Thread Local Storage )的内容
TlsSetValue 设定某个TLS (Thread Local Storage )的内容
WaitForInputIdle 等待,直到不再有输入消息进入某个执行线程中
图14…4 与执行线程有关的Win32 API 函数
753
…………………………………………………………Page 816……………………………………………………………
第篇 深入 MFC 程式設計
注意,多执行线程并不能让程序执行得比较快(除非是在多CPU 机器上,并且使用支持
symmetric multiprocessing 的操作系统),只是能够让程序比较「有反应」。试想某个程
式在某个菜单项目被按下后要做一个小时的运算工作,如果这份工作在主执行线程中做,
而且没有利用PeekMessage 的技巧时时观看消息队列的内容并处理之,那么这一个小时
内这个程序的使用者接口可以说是被冻结住了,将毫无反应。但如果沉重的运算工作是
由另一个执行线程来负责,使用者接口将依然灵活,不受影响。
Worker Threads 和 U I Threads
从Windows 操作系统的角度来看,执行线程就是执行线程,并未再有什么分类。但从MFC
的角度看,则把执行线程划分为和使用者接口无关的worker threads ,以及和使用者接口
(UI )有关的UI threads 。
基本上,当我们以::CreateThread 产生一个执行线程,并指定一个执行线程函数,它就是一
个worker thread ,除非在它的生命中接触到了输入消息…这时候它应该有一个消息回
路,以抓取消息,于是该执行线程摇身一变而为UI thread 。
注意,执行线程本来就带有消息队列,请看图14…3 的TDB 结构。而如果执行线程程序代码
中带有一个消息循环,就称为UI thread 。
错误观念
我记得曾经在微软的技术文件中,也曾经在微软的范例程序中,看到他们鼓励这样的作
法:为程序中的每一个窗口产生一个执行线程,负责窗口行为。这种错误的示范尤其存在
于MDI 程序中。是的,早期我也沾沾自喜地为MDI 程序的每一个子窗口设计一个执
行线程。基本上这是错误的行为,要付出昂贵的代价。因为子窗口一切换,上述作法会导
至执行线程也切换,而这却要花费大量的系统资源。比较好的作法是把所有UI (User
Interface )动作都集中在主执行线程中,其它的「纯种运算工作」才考虑交给worker threads
去做。
754
…………………………………………………………Page 817……………………………………………………………
14 MFC
第 章 多緒程式設計
正确态度
什么是使用多执行线程的好时机呢?如果你的程序有许多事要忙,但是你还要随时保持注
意某些外部事件(可能来自硬件或来自使用者),这时就适合使用多执行线程来帮忙。
以通讯程序为例。你可以让主执行线程负责使用者接口,并保持中枢的地位。而以一个分
离的执行线程处理通讯端口,
MFC 多线程程序设计
我已经在第1章以一个小节介绍了Win32 多线程程序的写法,并给了一个小范例
MltiThrd 。这一节,我要介绍MFC 多线程程序的写法。
探索CWinThread
就像CWinApp 对象代表一个程序本身一样,CWinThread 对象代表一个执行线程本身。这
个MFC 类别我们曾经看过,第6章讲「MFC 程序的生死因果」时,讲到「CWinApp::Run
程序生命的活水源头」,曾经追踪过CWinApp::Run 的源头CWinThread::Run (里面有
一个消息循环)。可见程序的「执行事实」系发生在CWinThread 对象身上,而CWinThread
对象必须要(必然会)产生一个执行线程。
我希望「CWinThread 对象必须要(必然会)产生一个执行线程」这句话不会引起你的误会,
以为程序在application object (CWinApp 对象)的构造式必然有个动作最终调用到
CreateThread 或_beginthreadex 。不,不是这样。想想看,当你的Win32 程序执行起来,
你的程序并没有调用CreateProcess 为自己做出代表自己的那个进程,也没有调用
CreateThread 为自己做出代表自己的主执行线程(primary thread )的那个执行线程。为你的
程序产生第一个进程和执行线程,是系统加载器以及核心模块(KERNEL32 )合作的结果。
所以,再次循着第6章一步步剖析的步骤,MFC 程序的第一个动作是CWinApp::CWinApp
(比WinMain 还早),在那里没有「产生执行线程」的动作,而是已经开始在收集执行线程
755
…………………………………………………………Page 818……………………………………………………………
第篇 深入 MFC 程式設計
的相关信息了:
// in MFC 4。2 APPCORE。CPP
CWinApp::CWinApp(LPCTSTR lpszAppName)
{
。。。
// initialize CWinThread state
AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
AFX_MODULE_THREAD_STATE* pThreadState = pModuleState…》m_thread;
ASSERT(AfxGetThread() == NULL);
pThreadState…》m_pCurrentWinThread = this;
ASSERT(AfxGetThread() == this);
m_hThread = ::GetCurrentThread();
m_nThreadID = ::GetCurrentThreadId();
。。。
}
虽然MFC 程序只会有一个CWinApp 对象,而CWinApp 衍生自CWinThread,但并不
是说一个MFC 程序只能有一个CWinThread 对象。每当你需要一个额外的执行线程,不
应该在MFC 程序中直接调用::CreateThread 或_beginthreadex,应该先产生一个
CWinThread 对象,再调用其成员函数CreateThread 或全域函数AfxBeginThread 将执行
线程产生出来。当然, 现在你必然已经可以推测到,CWinThread::CreateThread 或
AfxBeginThread 内部调用了::CreateThread 或_beginthreadex (事实上答案是
_beginthreadex )。
这看起来颇有值得商议之处:为什么CWinThread 构造式不帮我们调用AfxBeginThread
呢?似乎CWinThread 为德不卒。
图14…5 就是CWinThread 的相关源代码。
756
…………………………………………………………Page 819……………………………………………………………
14 MFC
第 章 多緒程式設計
#0001 // in MFC 4。2 THRDCORE。CPP
#0002 CWinThread::CWinThread(AFX_THREADPROC pfnThreadProc; LPVOID pParam)
#0003 {
#0004 m_pfnThreadProc = pfnThreadProc;
#0005 m_pThreadParams = pParam;
#0006
#0007 monConstruct();
#0008 }
#0009
#0010 CWinThread::CWinThread()
#0011 {
#0012 m_pThreadParams = NULL;
#0013 m_pfnThreadProc = NULL;
#0014
#0015 monConstruct();
#0016 }
#0017
#0018 void CWinThread::monConstruct()
#0019 {
#0020 m_pMainWnd = NULL;
#0021 m_pActiveWnd = NULL;
#0022
#0023 // no HTHREAD until it is created
#0024 m_hThread = NULL;
#0025 m_nThreadID = 0;
#0026
#0027 // initialize message pump
#0028 #ifdef _DEBUG
#0029 m_nDisablePumpCount = 0;
#0030 #endif
#0031 m_msgCur。message = WM_NULL;
#0032 m_nMsgLast = WM_NULL;
#0033 ::GetCursorPos(&m_ptCursorLast);
#0034
#0035 // most threads are deleted when not needed
#0036 m_bAutoDelete = TRUE;
#0037
#0038 // initialize OLE state
#0039 m_pMessageFilter = NULL;
#0040 m_lpfnOleTermOrFreeLib = NULL;
#0041 }
#0042
#0043 CWinThread* AFXAPI AfxBeginThread (AFX_THREADPROC pfnThreadProc; LPVOID pParam;
#0044 int nPriority; UINT nStackSize; DWORD dwCreateFlags;
#0045 LPSECURITY_ATTRIBUTES lpSecurityAttrs)
#0046 {
757
…………………………………………………………Page 820……………………………………………………………
第篇 深入 MFC 程式設計
#0047 CWinThread* pThread = DEBUG_NEW CWinThread (pfnThreadProc; pParam);
#0048
#0049 if (!pThread…》CreateThread (dwCreateFlags|CREATE_SUSPENDED; nStackSize;
#0050 lpSecurityAttrs))
#0051 {
#0052 pThread…》Delete();
#0053 return NULL;
#0054 }
#0055 VERIFY(pThread…》SetThreadPriority(nPriority));
#0056 if (!(dwCreateFlags & CREATE_SUSPENDED))
#0057 VERIFY(pThread…》ResumeThread() != (DWORD)…1);
#0058
#0059 return pThread;
#0060 }
#0061
#0062 CWinThread* AFXAPI AfxBeginThread (CRuntimeClass* pThreadClass;
#0063 int nPriority; UINT nStackSize; DWORD dwCreateFlags;
#0064 LPSECURITY_ATTRIBUTES lpSecurityAttrs)
#0065 {
#0066 ASSERT(pThreadClass != NULL);
#0067 ASSERT(pThreadClass…》IsDerivedFrom(RUNTIME_CLASS(CWinThread)));
#0068
#0069 CWinThread* pThread = (CWinThread*)pThreadClass…》CreateObject();
#0070
#0071 pThread…》m_pThreadParams = NULL;
#0072 if (!pThread…》CreateThread (dwCreateFlags|CREATE_SUSPENDED; nStackSize;
#0073 lpSecurityAttrs))
#0074 {
#0075 pThread…》Delete();
#0076 return NULL;
#0077 }
#0078 VERIFY(pThread…》SetThreadPriority(nPriority));
#0079 if (
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!