线程创建函数 CreateThread

创建步骤

CreateThread 将在主线程的基础上创建一个新线程,大致做如下步骤:
1.在内核对象中分配一个线程标识/句柄,可供管理,由CreateThread返回
2.把线程退出码置为STILL_ACTIVE,把线程挂起计数置1
3.分配context结构
4.分配两页的物理存储以准备栈,保护页设置为PAGE_READWRITE,第2页设为PAGE_GUARD
5.lpStartAddr和lpvThread值被放在栈顶,使它们成为传送给StartOfThread的参数
6.把context结构的栈指针指向栈顶(第5步)指令指针指向startOfThread函数

函数原型

1
2
3
4
5
6
7
8
CreateThread(
_In_opt_LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_SIZE_T dwStackSize,
_In_LPTHREAD_START_ROUTINE lpStartAddress,
_In_opt___drv_aliasesMemLPVOID lpParameter,
_In_DWORD dwCreationFlags,
_Out_opt_LPDWORD lpThreadId
);

参数说明

  1. lpThreadAttributes:指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,NULL使用默认安全性,不可以被子线程继承,否则需要定义一个结构体将它的bInheritHandle成员初始化为TRUE

  2. dwStackSize:设置初始栈的大小,以字节为单位,如果为0,那么默认将使用与调用该函数的线程相同的栈空间大小。任何情况下,Windows根据需要动态延长堆栈的大小。

  3. lpStartAddress:指向线程函数的指针,形式:@函数名,函数名称没有限制,
    线程有两种声明方式
    (1)DWORD WINAPI 函数名 (LPVOID lpParam); //标准格式

    1
    2
    3
    4
    5
    6
    DWORD WINAPI 函数名 (LPVOID lpParam)
    {
    return 0;
    }

    CreateThread(NULL, 0, 函数名, 0, 0, 0);

    (2)void 函数名();
    使用void 函数名()此种线程声明方式时,lpStartAddress需要加入LPTHREAD_START_ROUTINE转换,如

    1
    2
    3
    4
    5
    6
    void 函数名()
    {
    return;
    }

    CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)函数名, 0, 0, 0);
  4. lpParameter:向线程函数传递的参数,是一个指向结构的指针,不需传递参数时,为NULL。

  5. 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上不支持。

  6. lpThreadId:保存新线程的id。

返回值:函数成功,返回线程句柄;函数失败返回false。若不想返回线程ID,设置值为NULL。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<Windows.h>
#include<stdio.h>

DWORD WINAPI ThreadFunc(LPVOID);
void ThreadFunc1();

void main()
{
HANDLE hThread;
DWORD threadId;

hThread = CreateThread(NULL, 0, ThreadFunc, 0, 0, &threadId); // 创建线程
CreateThread(NULL, 0, ThreadFunc, 0, 0, &threadId);
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFunc1, 0, 0, &threadId);
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFunc1, 0, 0, &threadId);
printf("我是主线程, pid = %d\n", GetCurrentThreadId()); //输出主线程pid
Sleep(3000);
}

DWORD WINAPI ThreadFunc(LPVOID p)
{
printf("我是子线程1, pid = %d\n", GetCurrentThreadId()); //输出子线程pid
return 0;
}

void ThreadFunc1()
{
printf("我是子线程2, pid = %d\n", GetCurrentThreadId()); //输出子线程pid
}

输出如下:

1
2
3
4
5
我是主线程, pid = 10908
我是子线程1, pid = 11776
我是子线程1, pid = 13092
我是子线程2, pid = 13324
我是子线程2, pid = 12360