(VC/MFC)多线程(Multi-Threading) -1. 基本概念.

本文介绍了MFC框架下创建和管理线程的方法,包括工作者线程和用户界面线程的区别与实现。详细讲解了AfxBeginThread函数的使用,以及如何通过CWinThread类控制线程的行为。

在Win32环境中,每个运行的应用程序都建立一个进程(Process),每个进程有一个或多个执行线程(Thread)组成.

MFC把执行的线程封装在CWinThread类中,它还包括了同步类,这些类封装了事件,互斥,和可在Windows核心中找到的其他线程同步对象。

MFC区分了两种不同类型的线程: 用户界面线程(user-interface thread)工作者线程(worker thread).
两者的主要区别在于user-interface thread有消息循环,而工作者线程没有。
user-interface thread 可以创建窗口和处理发送给这些窗口的消息。worker thread执行后台任务,这些后台任务不直接接受用户的输入,因此不需要窗口和消息循环。

user-interface thread 最常用的是创建多窗口,这些窗口有分开的执行线程来负责管理。
worker thread 适合于执行孤立的任务,这些任务能够与应用程序的其他部分相脱离,并且当其他处理在前台发生时它在后台执行。


创建worker thread
-法1)构造一个CWinThread对象,并调用该对象的CreateThread()函数来创建线程。
-法2)用AfxBeginThread()构造一个CWinThread对象并同时创建一个线程。
  ; 注意,不要使用Win32的::CreteThread()函数在MFG程序中创建线程。Win32的这个API 和上述的 CWinThread::CreateThread(),AfxBeginThread()有所不同。
上述的2种方法除了启动线程外,还初始化被框架使用的内部变量,在线程创建过程期间的各个点上执行安全性检查,并采取步骤保用一个线程安全的访问C运行库中的函数。

//启动一个worker thread, 并传给它一个应用程序定义的数据结构ThreadInfo的地址。
CwinThread *pThread=AfxBeginThread(ThreadFunc,&ThreadInfo);

//ThreadFunc 是线程函数,当线程开始执行时,该函数得以执行。
UNIT ThreadFunc(LPVOID pParam){
 UNIT nlterations=(UNIT)pParam;
 for(UNIT i=0;i<nlterations;i++)
  return 0;
}

AfxBeginThread()的原型如下:
CWinThread* AfxBeginThread(
 AFX_THREADPROC pfnThreadProc,                     //线程的入口函数,声明一定要如下: UINT MyThreadFunction( LPVOID pParam ),不能设置为NULL;
 LPVOID pParam,                                                         //传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程.
 int nPriority=THREAD_PRIORITY_NORMAL,          //该线程的执行优先级。
 UNIT nStackSize=0,                                                    //该线程的最大堆栈尺寸。
 DWORD dwCreateFlags=0,                                     //默认值0,告诉系统立即开始执行该线程。
 LPSCURITY_ATTRIBUTES lpSecurityAttrs=NULL         
);

创建user-interface 线程.
- 与创建worker thread 的过程不同,一个worker thread是由它的线程函数定义,但是一个user-interface thread是由一个动态可创建的类来控制,
  该类是从CWinThread派生的。
- 一个CUIThread是通过调用AfxBeginThread()来启动的,该函数接受指向该线程类的一个CRuntimeClass指针。
  CWinThread *pThread=AfxBeginThread(RUNTIME_CLASS(CUIThread));


AfxBeginThread() 创建线程详解 

在MFC中,一般用全局函数AfxBeginThread()来创建并初始化一个线程的运行,该函数有两种重载形式,分别用于创建工作者线程和用户界面线程。两种重载函数原型和参数分别说明如下: 

(1) CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc,
                      LPVOID pParam,
                      nPriority=THREAD_PRIORITY_NORMAL,
                      UINT nStackSize=0,
                      DWORD dwCreateFlags=0,
                      LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);

PfnThreadProc:指向工作者线程的执行函数的指针,线程函数原型必须声明如下: UINT ExecutingFunction(LPVOID pParam);

请注意,ExecutingFunction()应返回一个UINT类型的值,用以指明该函数结束的原因。一般情况下,返回0表明执行成功。 

pParam:传递给线程函数的一个32位参数,执行函数将用某种方式解释该值。它可以是数值,或是指向一个结构的指针,甚至可以被忽略; 

nPriority:线程的优先级。如果为0,则线程与其父线程具有相同的优先级; 

nStackSize:线程为自己分配堆栈的大小,其单位为字节。如果nStackSize被设为0,则线程的堆栈被设置成与父线程堆栈相同大小; 

dwCreateFlags:如果为0,则线程在创建后立刻开始执行。如果为CREATE_SUSPEND,则线程在创建后立刻被挂起; 

lpSecurityAttrs:线程的安全属性指针,一般为NULL; 
 (2) CWinThread* AfxBeginThread(CRuntimeClass* pThreadClass,
                      int nPriority=THREAD_PRIORITY_NORMAL,
                      UINT nStackSize=0,
                      DWORD dwCreateFlags=0,
                      LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);


  pThreadClass 是指向 CWinThread 的一个导出类的运行时类对象的指针,该导出类定义了被创建的用户界面线程的启动、退出等;其它参数的意义同形式1。使用函数的这个原型生成的线程也有消息机制,在以后的例子中我们将发现同主线程的机制几乎一样。

下面我们对CWinThread类的数据成员及常用函数进行简要说明。 

   m_hThread:当前线程的句柄; 
   m_nThreadID:当前线程的ID; 
   m_pMainWnd:指向应用程序主窗口的指针 
   BOOL CWinThread::CreateThread(DWORD dwCreateFlags=0,
   UINT nStackSize=0,
   LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);

  该函数中的dwCreateFlags、nStackSize、lpSecurityAttrs参数和API函数CreateThread中的对应参数有相同含义,该函数执行成功,返回非0值,否则返回0。
  一般情况下,调用AfxBeginThread()来一次性地创建并启动一个线程,但是也可以通过两步法来创建线程:首先创建CWinThread类的一个对象,然后调用该对象的成员函数CreateThread()来启动该线程。 

virtual BOOL CWinThread::InitInstance();

  重载该函数以控制用户界面线程实例的初始化。初始化成功则返回非0值,否则返回0。用户界面线程经常重载该函数,工作者线程一般不使用InitInstance()。 
virtual int CWinThread::ExitInstance();

  在线程终结前重载该函数进行一些必要的清理工作。该函数返回线程的退出码,0表示执行成功,非0值用来标识各种错误。同InitInstance()成员函数一样,该函数也只适用于用户界面线程。 


 


挂起和继续执行线程
-挂起: CWinThread::SuspendThread()
-继续: CWinthread::ResumeThread()

使线程睡眠.
-通过调用API函数 ::Sleep() , 例如 ::Sleep(1000)使自己睡眠。

终止一个线程:
-对于worker thread , 当一个worker thread的线程函数执行一个返回语句或者调用AfxEndThread()时,这个worker thread 要终止。
-对于user-interface thread, 当一个WM_QUIT消息被发送到它的消息队列中,或该线程中的一个函数调用AfxEndThread()时,该线程就被终止。
  ; 一个线程可以用API函数 ::PostQuitMessage()把一个WM_QUIT消息发送到自身程序上.
- AfxEndThrea()和 ::PostQuitMessage()返回一个32位的退出吗,在该线程被终止后,可以用GetExitCodeThread()来检索改码。
 DWORD dwExitCode;
 ::GetExitCodeThread(pThread->m_hThread,&dwExitCode);
   如果调用一个仍在执行的线程,则::GetExitcodeThread()把dwExitCode置为STILL_ACTIVE(0x103).

从一个线程终止另一个线程:
-一般来说,线程只能自我终止,如果想要线程A终止线程B,必须建立一个信号通知机制,运行线程A告诉线程B终止它自己。
//Thread A
static BOOL bContinue=TRUE;
CWinThread *pThread=AfxBeginThread(ThreadFunc,&bContinue);

//Do some other work

//Save the thread handle.
HANDLE hThrend=pThread-<m_hThread;

//Tell thread B to terminate
bContinue=FALSE;

//::WaitForSingleObject()一直处于等待状态,知道被指定的对象(另一个线程)输入一个信号为止
//当线程终止时,一个线程对象将由无信号变成有信号的。
//第一个参数:想要等待的对象的句柄。第二个参数:愿意等待的时间长度。
::WaitForSingleObject(hThread,INFINITE);

//Thread B
UINT ThreadFunc(LPVOID pParam){
 BOOL *pContinue=(BOOL *)pParam;
 while(*pContinue){
 // Do some work
 }
 return 0;
}

 

要结束线程的两种方式

  1 : 这是最简单的方式,也就是让线程函数执行完成,此时线程正常结束.它会返回一个值,一般0是成功结束,

  当然你可以定义自己的认为合适的值来代表线程成功执行.在线程内调用AfxEndThread将会直接结束线程,此时线

  程的一切资源都会被回收.

  2 : 如果你想让另一个线程B来结束线程A,那么,你就需要在这两个线程中传递信息.

  不管是工作者线程还是界面线程,如果你想在线程结束后得到它的结果,那么你可以调用:

  ::GetExitCodeThread函数

"g++" -ftemplate-depth-128 -O3 -finline-functions -Wno-inline -Wall -pthread -DBOOST_ALL_NO_LIB=1 -DBOOST_PYTHON_SOURCE -DBOOST_PYTHON_STATIC_LIB -DNDEBUG -I"." -I"/usr/include/python2.7" -c -o "bin.v2/libs/python/build/gcc-4.8.5/release/link-static/threading-multi/exec.o" "libs/python/src/exec.cpp" ...failed gcc.compile.c++ bin.v2/libs/python/build/gcc-4.8.5/release/link-static/threading-multi/exec.o... gcc.compile.c++ bin.v2/libs/python/build/gcc-4.8.5/release/link-static/threading-multi/object/function_doc_signature.o In file included from ./boost/python/detail/prefix.hpp:13:0, from ./boost/python/converter/registrations.hpp:8, from libs/python/src/object/function_doc_signature.cpp:9: ./boost/python/detail/wrap_python.hpp:50:23: 致命错误:pyconfig.h:没有那个文件或目录 # include <pyconfig.h> ^ 编译中断。 "g++" -ftemplate-depth-128 -O3 -finline-functions -Wno-inline -Wall -pthread -DBOOST_ALL_NO_LIB=1 -DBOOST_PYTHON_SOURCE -DBOOST_PYTHON_STATIC_LIB -DNDEBUG -I"." -I"/usr/include/python2.7" -c -o "bin.v2/libs/python/build/gcc-4.8.5/release/link-static/threading-multi/object/function_doc_signature.o" "libs/python/src/object/function_doc_signature.cpp" ...failed gcc.compile.c++ bin.v2/libs/python/build/gcc-4.8.5/release/link-static/threading-multi/object/function_doc_signature.o... ...skipped <pbin.v2/libs/python/build/gcc-4.8.5/release/link-static/threading-multi>libboost_python.a(clean) for lack of <pbin.v2/libs/python/build/gcc-4.8.5/release/link-static/threading-multi>numeric.o... ...skipped <pbin.v2/libs/python/build/gcc-4.8.5/release/link-static/threading-multi>libboost_python.a for lack of <pbin.v2/libs/python/build/gcc-4.8.5/release/link-static/threading-multi>numeric.o... ...skipped <p/usr/local/lib>libboost_python.a for lack of <pbin.v2/libs/python/build/gcc-4.8.5/release/link-static/threading-multi>libboost_python.a... ...failed updating 58 targets... ...skipped 12 targets. 执行命令sudo ./b2 install 后报错,如何修复
最新发布
09-20
<think> 我们正在解决Boost编译过程中出现的`pyconfig.h`缺失问题。根据引用[1][3],该错误通常与Python开发头文件未安装有关。以下是系统化解决方案: ### 问题本质分析 `pyconfig.h`是Python C API的核心头文件,位于`/usr/include/pythonX.Y/`目录。编译Boost.Python组件时需要访问此文件,缺失原因包括: 1. Python开发包未安装 2. Python版本不匹配(如系统默认Python与Boost检测版本不一致) 3. 多Python版本共存导致路径混乱 ### 修复方案(分三步) #### 步骤1:安装对应Python版本的开发包 ```bash # 确认当前默认Python版本(以3.x为例) python3 --version # 假设输出 Python 3.8.10 # 安装对应开发包(Ubuntu/Debian) sudo apt-get install python3.8-dev # CentOS/RHEL系统使用 sudo yum install python38-devel ``` **原理**:该包提供`/usr/include/python3.8/pyconfig.h`及全套C API头文件[^1] #### 步骤2:显式指定Python版本(如步骤1无效) 在`bootstrap.sh`中强制声明Python路径: ```bash # 查找Python解释器路径 which python3 # 假设输出 /usr/bin/python3.8 # 重新配置Boost编译系统 sudo ./bootstrap.sh --with-python=/usr/bin/python3.8 ``` **作用**:确保Boost.Python组件绑定到正确版本[^2] #### 步骤3:创建符号链接(极端情况) 当头文件不在标准路径时,手动建立链接: ```bash # 定位现有pyconfig.h(通常在新版本Python路径) find /usr -name pyconfig.h # 示例输出 /usr/include/python3.9/pyconfig.h # 创建版本兼容链接 sudo ln -s /usr/include/python3.9/pyconfig.h /usr/include/python3.8/pyconfig.h ``` > 注意:此操作仅当系统同时存在Python 3.8和3.9且默认版本冲突时使用 ### 验证修复 重新编译并检查输出: ```bash sudo ./b2 install | grep -i "python" # 期望输出:包括"Backing up existing Python installation",无错误信息 ``` ### 关键技术原理 1. **Boost.Python编译机制**: - `bootstrap.sh`自动检测系统Python环境 - 通过`#include <pyconfig.h>`调用Python C API - 依赖`PYTHON_INCLUDE`环境变量定义头文件路径 2. **Linux头文件搜索规则**: ```mermaid graph LR A[g++编译器] --> B[-I/usr/include/python3.8] A --> C[系统默认路径/usr/include] B --> D[找到pyconfig.h] C --> E[失败时触发fatal error] ``` ### 相关问题 1. 如何为不同Python版本编译Boost.Python? 2. 在多Python环境中如何管理Boost依赖? 3. 交叉编译时如何处理Python头文件缺失? 4. `bzlib.h`缺失问题(引用[3])与`pyconfig.h`有何关联? 5. 如何验证Boost.Python组件安装成功? </think> ### 解决 Boost 编译时 `pyconfig.h` 找不到的问题 这个问题是由于编译 Boost 时缺少 Python 开发头文件导致的。以下是完整的解决方案: #### 1. **安装 Python 开发头文件(核心步骤)** ```bash # 对于 Ubuntu/Debian 系统 sudo apt-get install python-dev # Python 2 sudo apt-get install python3-dev # Python 3 # 对于 CentOS/RHEL 系统 sudo yum install python-devel # Python 2 sudo yum install python3-devel # Python 3 ``` **原理**:`pyconfig.h` 是 Python 开发包的一部分,安装开发包会创建 `/usr/include/pythonX.Y/pyconfig.h` 文件路径[^1][^3]。 --- #### 2. **检查 Python 头文件路径** ```bash # 确认头文件是否存在 ls /usr/include/python*/pyconfig.h # 如果没有找到,尝试定位安装位置 sudo find / -name pyconfig.h 2>/dev/null ``` --- #### 3. **手动链接头文件(如果位置异常)** ```bash # 示例:将找到的头文件链接到标准位置 mkdir -p /usr/include/python3.6 sudo ln -s /path/to/found/pyconfig.h /usr/include/python3.6/ ``` > 此步骤仅在自动安装失败时使用,路径需匹配您的 Python 版本 --- #### 4. **重新编译 Boost** ```bash # 清理之前的编译 sudo ./b2 clean # 重新配置并编译(添加详细日志) sudo ./bootstrap.sh --with-python=/usr/bin/python3 sudo ./b2 install -j4 | tee build.log # 输出日志便于调试 ``` --- ### 关键原理说明 1. **依赖关系**: - Boost.Python 组件需要访问 Python C API - `pyconfig.h` 是 Python 解释器配置的核心头文件 - 缺失时会导致编译链断裂:`g++ → #include <pyconfig.h> → 文件未找到` 2. **版本匹配要求**: $$ \text{Python 版本} \approx \text{Boost.Python 接口版本} $$ 使用 `--with-python` 参数确保版本一致 --- ### 相关问题 1. 如何为特定 Python 版本(如 3.9)编译 Boost? 2. 交叉编译时如何处理 `pyconfig.h` 路径问题? 3. 如何验证 Boost.Python 组件是否安装成功? 4. 在多 Python 版本环境中如何避免头文件冲突? 5. `bzlib.h` 缺失错误(引用[3])与 `pyconfig.h` 错误有何关联?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值