线程是程序执行的基本原子单位,是进程的一个实体,是CPU调度和分派的基本单位。一个进程可以由多个线程组成。每个线程都有自己的寄存器组,堆栈,输出机制和一个私有消息队列。
多线程可以实现并行的处理,避免了某一项任务长时间的占用CPU的时间,从而导致了其他线程闲置的情况。我们在进行多线程编程的时候要注意,当两个线程优先级非常高的时候,他们抢夺CPU的控制权,在线程切换的时候会消耗很多的CPU资源,会降低系统的性能。
先来看一下创建线程函数:
HANDLE CreateThread()
{
LPSECURITY_ATTRIBUTES LPThreadAttributes, //指向SECURITY_ATTRIBUTES的指针
SIZE_T dwStackSize, //表示线程为自己所用堆栈分配的地址空间的大小 系统缺省值为0
LPTHREAD_START-TOUTINE lpStartAddress, //表示新线程开始执行时代码所在函数的地址 即线程函数名
LPVOID lpParameter, //是传入线程函数的参数
DWORD dwCreationFlags, //指定控制线程创建的附加标志 取0线程立即执行 取CREATE_SUSPENDED线程挂起
LPDWORD lpThreadld //是个DWORD类型的地址,返回赋给该新线程的ID
}
线程函数lpParameter必须有以下原形:DWORD WINAPI XXXThreadFun(LPVOID lpParameter) { return(0); }
下面我们来创建一个线程:
#include <windows.h> #include <stdio.h> DWORD WINAPI ThreadFunc( LPVOID lpParam ) //线程函数,跟普通的函数没什么两样 { printf( "Parameter = %d.", *(DWORD*)lpParam ); return 0; } VOID main( VOID ) { DWORD dwThreadId, dwThrdParam = 1; HANDLE hThread; hThread = CreateThread( NULL,0,ThreadFunc,&dwThrdParam, 0,&dwThreadId); if (hThread == NULL) { printf( "CreateThread failed (%d)\n", GetLastError() ); } else { _getch(); CloseHandle( hThread ); } }
当我们使用线程的时候要注意,最好的方式是让线程自然结束。也可以人工的调用TerminateThread函数和ExitThread函数来结束线程,但是当我们用这种方式结束线程的时候,系统不会释放线程使用的堆栈,所以建议大家在编程的时候尽量让线程自己退出。
当我们调用函数退出线程的时候,可能会导致一下几种问题:
1、目标线程拥有临界区,那么windows系统就不会释放临界区。
2、如果线程正在执行某种内核调用,那么该线程的进程内核状态可能不合理。
3、如果目标线程正在操纵共享动态链接库的全局状态,那么windows系统可能破坏动态链接库的状态,从而影响该动态链接库的其他使用者。
操作系统在创建线程的时候有6个步骤,分别是:
1、分配线程内核对象标识和管理新创建的线程,内核对象保存很多系统信息来管理该线程,程内核对象的句柄是CreateThread函数的返回值。
2、把线程的退出代码初始化为STILL_ACTIVE,并把线程的挂起计数设为1.
3、为新线程分配CONTEXT结构。
4、通过保留地址空间区域、为该区域提交两页物理存储器、把提交的存储器的保护标志设为PAGE_READWRITE以及把第二页到顶部的页设为PAGE_GUARD属性来准备线程的堆栈。
5、把LpStartAddr和LopvThread的值放入堆栈的顶部,新线程将其看成传给StartofThread函数的参数。
6、初始化线程的CONTEXT结构中的堆栈指针寄存器,把它指向上步中windows系统放入堆栈的值,然后操作系统初始化执行指针寄存器,使其指向内部的函数,在windows系统执行线程启动函数的第一条执行前执行内部函数。
在上文中,我们一直说线程堆栈,线程堆栈,到底线程堆栈是如何确定其大小的呢?
当我们调用CreateThread函数的时候,在进程的内存地址空间中创建线程的堆栈,在函数中可以指定堆栈的大小,当创建线程以后就不能安全的改变线程的大小了,需要动态的向下增长堆栈。
2012/10/1
jofranks 于南昌