您的位置:首页 > 编程语言 > C语言/C++

c++学习笔记之线程基础

2012-12-17 23:06 746 查看
线程由两部分组成:

线程内核对象, 系统用此来管理线程,存放线程统计信息和线程上下文(指令指针寄存器、栈指针寄存器、其他存储器)。

线程栈, 用来维护线程执行时所需要的局部变量和函数的参数。

创建线程和创建进程的优劣:

进程与线程相比,进程耗费的系统资源更多,原因在于地址空间,系统会为进程创建一个虚拟的地址空间。

创建进程会发生大量记录活动,而线程不会涉及记录活动,所以创建进程会用到大量内存。

由于一个.exe 和 .dll文件要加载到一个地址空间,还要用到文件资源。

线程处理函数:

DWORD WINAPI ThreadProc ( PVOID pvParam) {

DWORD result = 0;

return result;

}

线程函数名可以修改,线程函数应尽量使用函数参数和局部变量,因为使用静态变量和全局变量时,多个线程可以同时访问这些变量,这样有可能会破坏变量的内容。如果必须要使用静态或全局变量,可以参考线程同步的内容。

创建线程:

HANDLE WINAPI CreateThread(
__in          LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in          SIZE_T dwStackSize,
__in          LPTHREAD_START_ROUTINE lpStartAddress,
__in          LPVOID lpParameter,
__in          DWORD dwCreationFlags,
__out         LPDWORD lpThreadId
);

lpThreadAttributes:指向一个SECURITY_ARRTIBUTES结构的一个指针,用做线程的安全属性,可以设置线程句柄的继承特性。

dwStackSize: 指定为其线程分配多少地址空间。如果传入0, 系统会根据/STACK链接开关指定的存储量来调拨存储器

lpStartAddress: 线程处理函数的地址

lpParameter: 线程处理函数的参数

dwCreationFlags: 线程创建成功后的标志,0为立即调度,CREATE_SUSPENDED为暂停调度,等待调用ResumeThread()函数,线程开始调度。

lpThreadId:返回线程id。可以传NULL,以表示不使用

函数返回线程内核对象句柄。

uintptr_t _beginthreadex( void *security,

unsigned stack_size,

unsigned ( *start_address )( void * ),

void *arglist,

unsigned initflag,

unsigned *thrdaddr );

函数参数与函数返回值与CreateThread一致

CreateThread函数与_beginthreadex函数的区别:

CreateThread函数是windows运行库的函数,而_beginthreadex是c/c++运行库的函数,每一个线程都有自己的专用_tiddata数据块.当用CreateThread创建线程时,线程不会分配这个数据块,而当线程调用一个需要_tiddata结构的c/c++运行库函数时,c/c++运行库函数会尝试取得线程数据块的地址(调用TlsGetValue).如果NULL被作为地址返回,表明主调线程没有相关联的_tiddata数据块。这时,c/c++运行库函数会为线程分配并初始化一个_tiddata块,然后与线程关联(调用TlsSetValue)。当用_beginthreadex创建线程时,_beginthreadex内部会先分配一个_tiddata数据块,并将线程函数地址和参数传给_tiddata数据块,然后调用CreateThread创建线程,为其线程函数传递_threadstartex函数,线程参数传递_tiddata数据块。而在_threadstartex函数中,将_tiddata数据块与线程想关联,然后调用_callthreadstartex函数,函数内部做了结构化异常处理(SEH),调用_tiddata数据保存的函数地址和参数以调用外部定义的线程函数。

最好用_beginthreadex函数而不用CreateThread函数的目的在于:当用CreateThread创建线程时

第一,如果线程使用了c/c++运行库的signal函数,进程会终止,因为SEH没有就绪。

第二,如果线程不是使用_endthreaex来终止的,_tiddata数据块将不会被释放,造成内存泄露。

终止线程:

终止线程有四种方式:

线程函数返回:当线程函数返回时,线程内的所有资源会被正确清理。c++对象通过析构函数被正确销毁,释放线程使用内存,设置线程退成代码,减少线程内核对象使用计数

调用ExitThread函数:主调线程立即终止,操作系统清理线程占用系统资源。参数DWORD为线程退出代码,内核对象使用计数递减。(此为windows运行库版本,应使用c/c++运行库的_endthreadex,原因如CreateThread 函数和_beginthreaex函数的区别一样)

调用TerminateThread函数:工作模式同ExitThread函数一样,区别在于可以终止任何线程,线程的堆栈不会被销毁,函数是异步的。即函数返回时,线程不一定已经停止运行。

进程被终止(即ExitProcess函数和TerminateProcess函数):两个函数将会终止进程内的所有线程,相当于调用多个 ExitThread函数和TerminateThread函数。、

线程终止时会进行的操作:

线程拥有的所有用户对象句柄会被释放。

线程退出代码从STILL_ACTIVE变成传给ExitThread或TerminateThread的代码

线程内核对象状态变为触发状态

如果线程时进程最后一个活动线程,系统认为进程也终止了

线程内核对象计数减1

线程内幕:

一调用CreateThread函数,系统会创建一个线程内核对象,该对象的使用计数为2,暂停计数为1,退出代码为STILL_ACTIVE,对象为未触发状态。一旦创建了线程内核对象,系统会分配内存供线程栈使用,此内存是从进程的地址空间分配的,因为线程没有地址空间。系统将两个值传入线程堆栈(线程参数、线程函数)。线程有自己的一组cpu寄存器,称为线程的上下文。上下文反映了当线程上一次执行时,线程的cpu寄存器状态。保存在一个CONTEXT结构中,CONTEXT结构本身保存在内核对象中。指令指针寄存器h和栈指针寄存器是线程上下文中最重要的两个寄存器,线程始终在上下文中运行。CONTEXT结构的堆栈指针寄存器(SP)被设为堆栈中线程函数的地址,而指令指针寄存器被设为RtlUserThreadStart(NTDLL.DLL模块导出)函数的地址。

VOID RtlUserThreadStart(PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam) {

__try {

ExitThread( (pfnStartAddr) (pvParam));

}

__except(UnhandledExceptionFilter(GetExceptionInformation () ) ) {

ExitProcess(GetExceptionCode() );

}

}

系统检查CREATE_SUSPENDED标志是否被传给CreateThread函数。没有传递,线程暂停计数递减至0,随后,线程调度给处理器去执行。然后,系统在实际的cpu寄存器中加载上一次在线程上下文中保存的值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: