标题：CreateThread
简介
微软在Windows API中提供了建立新的线程的函数CreateThread，
概述
当使用CreateProcess调用时，系统将创建一个进程和一个主线程。CreateThread将在主线程的基础上创建一个新线程，大致做如下步骤：
1在内核对象中分配一个线程标识/句柄，可供管理，由CreateThread返回
2把线程退出码置为STILL_ACTIVE，把线程挂起计数置1
3分配context结构
4分配两页的物理存储以准备栈，保护页设置为PAGE_READWRITE，第2页设为PAGE_GUARD
5lpStartAddr和lpvThread值被放在栈顶，使它们成为传送给StartOfThread的参数
6把context结构的栈指针指向栈顶（第5步）指令指针指向startOfThread函数
MSDN中CreateThread原型：
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, 
DWORD dwStackSize, 
LPTHREAD_START_ROUTINE lpStartAddress, 
LPVOID lpParameter, 
DWORD dwCreationFlags, 
LPDWORD lpThreadId);
参数说明
lpThreadAttributes：指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中，NULL使用默认安全性，不可以被子线程继承，否则需要定义一个结构体将它的bInheritHandle成员初始化为TRUE 
dwStackSize，设置初始栈的大小，以字节为单位，如果为0，那么默认将使用与调用该函数的线程相同的栈空间大小。任何情况下，Windows根据需要动态延长堆栈的大小。
lpStartAddress，指向线程函数的指针，形式：@函数名，函数名称没有限制，但是必须以下列形式声明：
DWORD WINAPI ThreadProc (LPVOID lpParam) ，格式不正确将无法调用成功。
//也可以直接调用void类型
//但lpStartAddress要这样通过LPTHREAD_START_ROUTINE转换如：(LPTHREAD_START_ROUTINE)MyVoid
//然后在线程声明为：
void MyVoid()
{
return;
}
lpParameter：向线程函数传递的参数，是一个指向结构的指针，不需传递参数时，为NULL。 
dwCreationFlags ：线程标志,可取值如下 
（1）CREATE_SUSPENDED(0x00000004)：创建一个挂起的线程， 
（2）0：表示创建后立即激活。 
（3）STACK_SIZE_PARAM_IS_A_RESERVATION(0x00010000)：dwStackSize参数指定初始的保留堆栈的大小，否则,dwStackSize指定提交的大小。该标记值在Windows 2000/NT and Windows Me/98/95上不支持。
lpThreadId:保存新线程的id。
返回值：
函数成功，返回线程句柄；函数失败返回false。
若不想返回线程ID,设置值为NULL。
函数说明：
创建一个线程。
语法：
hThread = CreateThread (&security_attributes, dwStackSize, ThreadProc,pParam, dwFlags, &idThread) ;
一般并不推荐使用 CreateThread函数，而推荐使用RTL 库里的System单元中定义的 BeginThread函数，因为这除了能创建一个线程和一个入口函数以外，还增加了几项保护措施。
在MFC程序中，应该调用AfxBeginThread函数，在Visual C++程序中应调用_beginthreadex函数。
内存泄漏
在很多参考书上，都说不要用CreateThread 创建线程、并用CloseHandle来关闭这个线程，因为这样做会导致内存泄漏，而应该用_beginthread来创建线程，_endthread来销毁线程。其实，真正的原因并非如此。看如下一段代码：
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全属性
DWORD dwStackSize, // 堆栈大小
LPTHREAD_START_ROUTINE lpStartAddress, // 线程函数
LPVOID lpParameter, //线程参数
DWORD dwCreationFlags, // 线程创建属性 
LPDWORD lpThreadId // 线程ID
);
线程中止运行后，线程对象仍然在系统中，必须通过CloseHandle函数来关闭该线程对象。CloseHandle函数的原型是：
BOOL CloseHandle( HANDLE hObject );//HANDLE hObject 对象句柄 
CloseHandle可以关闭多种类型的对象，比如文件对象等，这里使用这个函数来关闭线程对象。调用时，hObject为待关闭的线程对象的句柄。
说使用这种方法可能会引发内存泄漏问题，其实不完全正确。那为什么会引起内存的泄漏呢？因为当线程的函数用到了C的标准库的时候，很容易导致冲突，所以在创建VC的工程时，系统提示是用单线程还是用多线程的库，因为在C的内部有很多的全局变量。例如，出错号、文件句柄等全局变量。
因为在C的库中有全局变量，这样用C的库时，如果程序中使用了标准的C程序库时，就很容易导致运行不正常，会引起很多的冲突。所以，微软和Borland都对C的库进行了一些改进。但是这个改进的一个条件就是，如果一个线程已经开始创建了，就应该创建一个结构来包含这些全局变量，接着把这些全局变量放入线程的上下文中和这个线程相关起来。这样，全局变量就会依赖于这个线程，不会引起冲突。
这样做就会有一个问题，什么时候这个线程开始创建呢？标准的Windows的API是不知道的，因为它是静态的库。这些库都是放在VC的LIB的目录内的，而线程函数是操作系统的函数。所以，VC和BC在创建线程时，都会用_beginThread来创建线程，再用_endThread来结束线程。这样，它们在创建线程的时候，就会知道什么时候创建了线程，并把全局变量放入某一结构中，让它和线程能关联起来。这样就不会发生冲突了。
很显然，要完成这个功能，首先需要分配结构表把全局变量包含起来。这个过程是在_beginThread时做的，而释放在_endTread内完成。
所以，当用_beginThread来创建，而用CloseHandle来关闭线程时，这时复制的全局结构就不会被释放了，这就有了内存的泄漏。这就是很多资料所说的内存泄漏问题的真正的原因。
其实，可以不用_beginThread和_endThread这一对函数。如果用CreateThread函数创建，用CloseHandle关闭，那么，与C有关的库就会用全局的，它们会引起冲突。所以，比较好的方法就是在线程内不用标准的C的库（可以使用Windows API的库函数）。这样就不会有什么问题，也就不会引起冲突。例如，字符串的操作函数、文件操作等。
当某个程序创建一个线程后，会产生一个线程的句柄，线程的句柄主要用来控制整个线程的运行，例如停止、挂起或设置线程的优先级等操作。
示例
CreateThread 函数从一个进程里面创建一个线程。这个开始的线程必须指定开始执行代码的地址，新线程执行。有代表性的，开始地址就是一个函数名。这个函数有一个参数，并且返回一个 DWORD 值。一个进程里面同时有多个线程在执行。
下面这个例子演示如何创建一个新线程，执行本地定义的函数。 ThreadProc. 建立的线程动态分配内存传递信息到每个线程的实例中。线程函数负责释放这些内存。
被调用的线程用 WaitForMultipleObjects 持续等待，直到所有的工作线程退出。在线程退出后，关掉线程函数的句柄。
#include <windows.h>
#include <strsafe.h> //win2003 SDK必须安装　要不无此头文件。此文件是为了实现StringCchPrintf，StringCchLength。
#define MAX_THREADS 3
#define BUF_SIZE 255
typedef struct _MyData {
int val1;
int val2;
} MYDATA, *PMYDATA;
DWORD WINAPI ThreadProc( LPVOID lpParam ) 
{ 
HANDLE hStdout;
PMYDATA pData;
TCHAR msgBuf[BUF_SIZE];
size_t cchStringSize;
DWORD dwChars;
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
if( hStdout == INVALID_HANDLE_VALUE )
return 1;
// Cast the parameter to the correct data type.
pData = (PMYDATA)lpParam;
// Print the parameter values using thread-safe functions.
StringCchPrintf(msgBuf, BUF_SIZE, TEXT("Parameters = %d, %d\n"), 
pData->val1, pData->val2); 
StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
WriteConsole(hStdout, msgBuf, cchStringSize, &dwChars, NULL);
// Free the memory allocated by the caller for the thread 
// data structure.
HeapFree(GetProcessHeap(), 0, pData);
return 0; 
} 
void main()
{
PMYDATA pData;
DWORD dwThreadId[MAX_THREADS];
HANDLE hThread[MAX_THREADS]; 
int i;
// Create MAX_THREADS worker threads.
for( i=0; i<MAX_THREADS; i++ )
{
// Allocate memory for thread data.
pData = (PMYDATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(MYDATA));
//5);
if( pData == NULL )
ExitProcess(2);
// Generate unique data for each thread.
pData->val1 = i;
pData->val2 = i+100;
hThread[i] = CreateThread( 
NULL, // default security attributes
0, // use default stack size 
ThreadProc, // thread function 
pData, // argument to thread function 
0, // use default creation flags 
&dwThreadId[i]); // returns the thread identifier 
// Check the return value for success. 
if (hThread[i] == NULL) 
{
ExitProcess(i);
}
}
// Wait until all threads have terminated.
WaitForMultipleObjects(MAX_THREADS, hThread, TRUE, INFINITE);
// Close all thread handles upon completion.
for(i=0; i<MAX_THREADS; i++)
{
CloseHandle(hThread[i]);
}
}