c++的进程和线程【windows】
2015-07-13 15:40
323 查看
前几天搞定了C++的多线程编程,今天闲来无事小做总结!
【线程的概念】
为了了解线程的概念,我们必须先讨论一下进程的概念。
一个进程通常定义为程序的一个实例。在Win32中, 进程占据4GB的地址空间。与它们在MS-DOS和16位Windows操作系统中不同, Win32进程是没有活力的。这就是说,一个Win32进程并不执行什么指令,它只是占据着4GB的地址空间,此空间中有应用程序EXE文件的 代码和数据。EXE需要的任意DLL也将它们的代码和数据装入到进程的地址空间。除了地址空间,进程还占有某些资源,比如文件、动态内存分配和线程。当进程终止时,在它生命期中创建的各种资源将被清除。
但是进程是没有活力的,它只是一个静态的概念。为了让进程完成一些工作,进程必须至少占有一个线程,所以线程是描述进程内的执行,正是线程负责执行包含在进程的地址空间中的代码。实际上,单个进程可以包含几个线程, 它们可以同时执行进程的地址空间中的代码。为了做到这一点,每个线程有自己的一组CPU寄存器和堆栈。
每个进程至少有一个线程在执行其地址空间中的代码,如果没有线程执行进程 地址空间中的代码, 进程也就没有继续存在的理由,系统将自动清除进程及其地址空间。为了运行所有这些线程,操作系统为每个独立线程安排一些CPU 时间,操作系统以轮转方式向线程提供时间片,这就给人一种假象,好象这些线程都在同时运行。创建一个Win32进程时,它的第一个线程称为主线程,它 由系统自动生成,然后可由这个主线程生成额外的线程,这些线程,又可生成更多的线程。
【多线程编程的步骤】
1、编写线程函数
所有线程必须从一个指定的函 数开始执行,该函数称为线程函数,它必须具有下列原型:
DWORDWINAPIYourThreadFunc(LPVOIDlpvThreadParm);
该函数输入一个LPVOID型的参数,可以是一个DWORD型的整数,也可以是一个指向一个缓冲区的指针, 返回一个DWORD型的值。象WinMain函数一样,这个函数并不由操作系统调用, 操作系统调用包含在KERNEL32.DLL中的非C运行时的一个内部函数,如StartOfThread,然后由StartOfThread函数建立起一个异常处理框架后,调用我们的函数。
以下代码为两个线程函数:
void FunCount(PVOID arg)
{
while(1)
{
printf("funcout is executing.....\n");
Sleep(10000);
}
}
void Function1(PVOID arg)
{
while(1)
{
printf("function1 is executing.....\n");
Sleep(20000);
}
}
2、创建一个线程
一个进程的主线程是由操作系统自动生成,如果你要让一个主线程创建额外的线程,你可以调用来CreateThread完成。
HANDLECreateThread(LPSECURITY_ATTRIBUTES lpsa,DWORDcbstack,LPTHREAD_START_ROUTINElpStartAddr,
LPVOID lpvThreadParm,DWORDfdwCreate,LPDWORDlpIDThread);
其中lpsa参数为一个指向SECURITY_ATTRIBUTES结构的指针。如果想让对象为缺省安全属性的话,可以传一个NULL,如果想让任一个子进程都可继承一个该线程对象句柄,必须指定一个SECURITY_ATTRIBUTES结构,其中bInheritHandle成员初始化为TRUE。参数cbstack表示线程为自己所用堆栈分配的地址空间大小,0表示采用系统缺省值。
参数lpStartAddr用来表示新线程开始执行时代码所在函数的地址,即为线程函数。lpvThreadParm为传入线程函数的参数,fdwCreate参数指定控制线程创建的附加标志,可以取两种值。如果该参数为0,线程就会立即开始执行,如果该参数为CREATE_SUSPENDED,则系统产生线程后,初始化CPU,登记CONTEXT结构的成员,准备好执行该线程函数中的第一条指令,但并不马上执行,而是挂起该线程。最后一个参数lpIDThread 是一个DWORD类型地址,返回赋给该新线程的ID值。
此外,还可以使用_beginthread等函数来创建线程。
handle=(HANDLE)_beginthread(FunCount,0, NULL);
handle1=(HANDLE)_beginthread(Function1,0,NULL);
记得要把process.h头文件include进来哦!
3、终止线程
如果某线程调用了ExitThread 函数,就可以终止自己。
VOIDExitThread(UINTfuExitCode );
这个函数为调用该函数的线程设置了退出码fuExitCode后, 就终止该线程。调用TerminateThread函数亦可终止线程。
BOOLTerminateThread(HANDLE hThread,DWORDdwExitCode);
该函数用来结束由hThread参数指定的线程, 并把dwExitCode设成该线程的退出码。当某个线程不在响应时,我们可以用其他线程调用该函数来终止这个不响应的线程。
4、设定线程的相对优先级
当一个线程被首次创建时,它的优先级等同于它所属进程的优先级。在单个进程内可以通过调用SetThreadPriority函数改变线程的相对优先级。一个线程的优先级是相对于其所属的进程的优先级而言的。
BOOLSetThreadPriority(HANDLE hThread,intnPriority);
其中参数hThread是指向待修改 优先级线程的句柄,nPriority可以是以下的值:
THREAD_PRIORITY_LOWEST,
THREAD_PRIORITY_BELOW_NORMAL,
THREAD_PRIORITY_NORMAL,
THREAD_PRIORITY_ABOVE_NORMAL,
THREAD_PRIORITY_HIGHEST
5、挂起及恢复线程
先前我提到过可以创建挂起状态的线程(通过传递CREATE_SUSPENDED标志给函数CreateThread来实现)。当你这样做时,系统创建指定线程的核心对象,创建线程的栈,在CONTEXT结构中初始化线程CPU注册成员。然而,线程对象被分配了一个初始挂起计数值1,这表明了系统将不再分配CPU去执行线程。要开始执行一个线程,另一个线程必须调用ResumeThread并传递给它调用CreateThread时返回的线程句柄。
DWORD ResumeThread(HANDLEhThread);
一个线程可以被挂起多次。如果一个线程被挂起3次, 则该线程在它被分配CPU之前必须被恢复3次。除了在创建线程时使用CREATE_SUSPENDED标志,你还可以用SuspendThread函数挂起线程。
DWORDSuspendThread(HANDLE hThread);
【线程的概念】
为了了解线程的概念,我们必须先讨论一下进程的概念。
一个进程通常定义为程序的一个实例。在Win32中, 进程占据4GB的地址空间。与它们在MS-DOS和16位Windows操作系统中不同, Win32进程是没有活力的。这就是说,一个Win32进程并不执行什么指令,它只是占据着4GB的地址空间,此空间中有应用程序EXE文件的 代码和数据。EXE需要的任意DLL也将它们的代码和数据装入到进程的地址空间。除了地址空间,进程还占有某些资源,比如文件、动态内存分配和线程。当进程终止时,在它生命期中创建的各种资源将被清除。
但是进程是没有活力的,它只是一个静态的概念。为了让进程完成一些工作,进程必须至少占有一个线程,所以线程是描述进程内的执行,正是线程负责执行包含在进程的地址空间中的代码。实际上,单个进程可以包含几个线程, 它们可以同时执行进程的地址空间中的代码。为了做到这一点,每个线程有自己的一组CPU寄存器和堆栈。
每个进程至少有一个线程在执行其地址空间中的代码,如果没有线程执行进程 地址空间中的代码, 进程也就没有继续存在的理由,系统将自动清除进程及其地址空间。为了运行所有这些线程,操作系统为每个独立线程安排一些CPU 时间,操作系统以轮转方式向线程提供时间片,这就给人一种假象,好象这些线程都在同时运行。创建一个Win32进程时,它的第一个线程称为主线程,它 由系统自动生成,然后可由这个主线程生成额外的线程,这些线程,又可生成更多的线程。
【多线程编程的步骤】
1、编写线程函数
所有线程必须从一个指定的函 数开始执行,该函数称为线程函数,它必须具有下列原型:
DWORDWINAPIYourThreadFunc(LPVOIDlpvThreadParm);
该函数输入一个LPVOID型的参数,可以是一个DWORD型的整数,也可以是一个指向一个缓冲区的指针, 返回一个DWORD型的值。象WinMain函数一样,这个函数并不由操作系统调用, 操作系统调用包含在KERNEL32.DLL中的非C运行时的一个内部函数,如StartOfThread,然后由StartOfThread函数建立起一个异常处理框架后,调用我们的函数。
以下代码为两个线程函数:
void FunCount(PVOID arg)
{
while(1)
{
printf("funcout is executing.....\n");
Sleep(10000);
}
}
void Function1(PVOID arg)
{
while(1)
{
printf("function1 is executing.....\n");
Sleep(20000);
}
}
2、创建一个线程
一个进程的主线程是由操作系统自动生成,如果你要让一个主线程创建额外的线程,你可以调用来CreateThread完成。
HANDLECreateThread(LPSECURITY_ATTRIBUTES lpsa,DWORDcbstack,LPTHREAD_START_ROUTINElpStartAddr,
LPVOID lpvThreadParm,DWORDfdwCreate,LPDWORDlpIDThread);
其中lpsa参数为一个指向SECURITY_ATTRIBUTES结构的指针。如果想让对象为缺省安全属性的话,可以传一个NULL,如果想让任一个子进程都可继承一个该线程对象句柄,必须指定一个SECURITY_ATTRIBUTES结构,其中bInheritHandle成员初始化为TRUE。参数cbstack表示线程为自己所用堆栈分配的地址空间大小,0表示采用系统缺省值。
参数lpStartAddr用来表示新线程开始执行时代码所在函数的地址,即为线程函数。lpvThreadParm为传入线程函数的参数,fdwCreate参数指定控制线程创建的附加标志,可以取两种值。如果该参数为0,线程就会立即开始执行,如果该参数为CREATE_SUSPENDED,则系统产生线程后,初始化CPU,登记CONTEXT结构的成员,准备好执行该线程函数中的第一条指令,但并不马上执行,而是挂起该线程。最后一个参数lpIDThread 是一个DWORD类型地址,返回赋给该新线程的ID值。
此外,还可以使用_beginthread等函数来创建线程。
handle=(HANDLE)_beginthread(FunCount,0, NULL);
handle1=(HANDLE)_beginthread(Function1,0,NULL);
记得要把process.h头文件include进来哦!
3、终止线程
如果某线程调用了ExitThread 函数,就可以终止自己。
VOIDExitThread(UINTfuExitCode );
这个函数为调用该函数的线程设置了退出码fuExitCode后, 就终止该线程。调用TerminateThread函数亦可终止线程。
BOOLTerminateThread(HANDLE hThread,DWORDdwExitCode);
该函数用来结束由hThread参数指定的线程, 并把dwExitCode设成该线程的退出码。当某个线程不在响应时,我们可以用其他线程调用该函数来终止这个不响应的线程。
4、设定线程的相对优先级
当一个线程被首次创建时,它的优先级等同于它所属进程的优先级。在单个进程内可以通过调用SetThreadPriority函数改变线程的相对优先级。一个线程的优先级是相对于其所属的进程的优先级而言的。
BOOLSetThreadPriority(HANDLE hThread,intnPriority);
其中参数hThread是指向待修改 优先级线程的句柄,nPriority可以是以下的值:
THREAD_PRIORITY_LOWEST,
THREAD_PRIORITY_BELOW_NORMAL,
THREAD_PRIORITY_NORMAL,
THREAD_PRIORITY_ABOVE_NORMAL,
THREAD_PRIORITY_HIGHEST
5、挂起及恢复线程
先前我提到过可以创建挂起状态的线程(通过传递CREATE_SUSPENDED标志给函数CreateThread来实现)。当你这样做时,系统创建指定线程的核心对象,创建线程的栈,在CONTEXT结构中初始化线程CPU注册成员。然而,线程对象被分配了一个初始挂起计数值1,这表明了系统将不再分配CPU去执行线程。要开始执行一个线程,另一个线程必须调用ResumeThread并传递给它调用CreateThread时返回的线程句柄。
DWORD ResumeThread(HANDLEhThread);
一个线程可以被挂起多次。如果一个线程被挂起3次, 则该线程在它被分配CPU之前必须被恢复3次。除了在创建线程时使用CREATE_SUSPENDED标志,你还可以用SuspendThread函数挂起线程。
DWORDSuspendThread(HANDLE hThread);
#include<Windows.h> #include<iostream> #include<process.h> using namespace std; struct myelem{ int a; char ch; }; void myfun(PVOID arg) { cout<<"myfun start..."<<endl; int i=100000; while(1){ cout<<"myfun "<<++i<<endl; Sleep(1000); } } void myfun2(PVOID arg) { cout<<"myfun2 start..."<<endl; int i=500; while(1){ cout<<"myfun2 "<<++i<<endl; Sleep(1000); if(i>=505)ExitThread(10); } } void myfun3(void *p) { cout<<"myfun3 start..."<<endl; myelem* myp=(myelem*)p; cout<<"myfun3 "<<myp->a<<" "<<myp->ch<<endl; ExitThread(111);//设置退出码 } int main() { int i=0; //_beginthread 函数 HANDLE handle=(HANDLE)_beginthread(myfun,0, NULL); DWORD sid=0; HANDLE handle2=(HANDLE)CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)myfun2,0,0,&sid); cout<<sid<<endl; myelem e; e.a=10;e.ch='A'; HANDLE handle3=(HANDLE)_beginthread(myfun3,0,&e);//传入参数 Sleep(1000); if(GetExitCodeThread(handle3,&sid))cout<<"sid "<<sid<<endl; TerminateThread(handle2,sid+10); Sleep(2000);//如果不加这一句下面的GetExitCodeThread获得的就是259 GetExitCodeThread(handle2,&sid);cout<<"sid "<<sid<<endl; SuspendThread(handle);//suspend Sleep(2000); while(1){ cout<<++i<<endl; Sleep(1000); if(i>2)ResumeThread(handle);//resume } }
相关文章推荐
- C++多线程编程简单实例
- C++ clock()函数实现功能介绍
- c语言中,指针加1的情况.指针变量详细介绍
- 学习C++的记录
- C语言中将字符串转换为整数
- C++ 拷贝构造函数
- printf的用法(此文章用于申请搜狐自媒体)作者胡金峰
- C++ 构造函数使用new
- c++变量的作用域、生存期和可见性
- char使用的常见问题
- C语言基础scanf的用法
- 【protobuf 001】 最常用的两种C++序列化方案的使用心得(protobuf和boost serialization)
- C语言概述
- C++11 lambda 表达式
- C语言运算符优先级列表
- C++中函数调用时的三种参数传递方式
- C++ COM组件相关
- c++动态库中使用命名空间的问题
- 使用 C++ 处理 JSON 数据交换格式
- DLL与LIB库文件的生成和使用---C++