- 先决条件
- 传统的创建方式
- 使用 CreateThread 函数
- 实例
- 更安全的方式
- _beginthreadex
- 实例
- 终止线程
- 补充
- WaitForMultipleObjects 函数
- 实例
- 参考
最好了解以下内容
- 了解内核对象
- 了解进程,线程的理论,基本知识
- 了解内存结构
- 了解堆栈
- 定义:该函数可以在进程的虚拟地址空间内创建线程
【注】要在 A 进程中创建 B 进程里的线程,可以使用CreateRemoteThread
函数 - 语法
HANDLE CreateThread(
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] SIZE_T dwStackSize,
[in] LPTHREAD_START_ROUTINE lpStartAddress,
[in, optional] __drv_aliasesMem LPVOID lpParameter,
[in] DWORD dwCreationFlags,
[out, optional] LPDWORD lpThreadId
);
参数说明
- lpThreadAttributes:线程的句柄是否可以被子进程继承,一般选择填 NULL,表示不可继承
- dwStackSize:初始化栈的大小,其单位是字节,如果参数为 0,表示使用默认的大小
- lpStartAddress:要执行的函数的地址
- lpParameter:要传入该函数的参数,这是一个指针结构
- dwCreationFlags:“创建标志位”,如果参数为 0 线程创建后立即执行,参数为
CREATE_SUSPENDED
表示线程创建后进入挂起状态,直到ResumeThread
函数调用 - lpThreadId:接收线程标识符,如果为 NULL,表示不接收。个人觉得这个就是线程的 id 号
- 返回值:如果函数成功执行则返回指向这个新创建的线程的句柄
【注】in 表示输入参数,out 表示输出参数,optional 表示参数可选
【注】即使 lpStartAddress
指向数据、代码或不可访问的区域,CreateThread
也可能会执行成功
【注】如果多线程要调用 CRT
,则应该使用 _beginthreadex
函数来创建线程,同时它具有更高的安全性
#include
#include
#define HANDLENUM 5
// function declaration
DWORD WINAPI MyThreadFunction(LPVOID lpParam);
// pass paramter
typedef struct PassValue{
int val1;
int val2;
}PASSVALUE, *PPASSVALUE;
void m_CreateThread()
{
HANDLE aHandleArray[HANDLENUM];
DWORD adwThreadID[HANDLENUM]; // receive thread descriptor
// fill paramter
PASSVALUE pv{
1,
2
};
// create threads
for (int i = 0; i < HANDLENUM; i++)
{
aHandleArray[i] = CreateThread(NULL, 0, MyThreadFunction, &pv, 0, &adwThreadID[i]);
}
// omit error handling ...
// wait for all thread execution completed
WaitForMultipleObjects(HANDLENUM, aHandleArray, TRUE, INFINITE);
printf("所有线程执行完毕\n");
// close handle
for (int i = 0; i < HANDLENUM; i++)
{
CloseHandle(aHandleArray[i]);
}
}
DWORD WINAPI MyThreadFunction(LPVOID lpParam)
{
PASSVALUE *pv = (PASSVALUE *)lpParam;
printf("thread id is %d, pass paramter one is: %d, pass paramter two is: %d\n", GetCurrentThreadId(), pv->val1, pv->val2);
return 0;
}
执行结果
thread id is 23408, pass paramter one is: 1, pass paramter two is: 2
thread id is 24404, pass paramter one is: 1, pass paramter two is: 2
thread id is 13612, pass paramter one is: 1, pass paramter two is: 2
thread id is 33796, pass paramter one is: 1, pass paramter two is: 2
thread id is 8032, pass paramter one is: 1, pass paramter two is: 2
所有线程执行完毕
更安全的方式
_beginthreadex
_beginthread 与其属于同一类方式,这里不单独说明
- 定义:创建线程的函数
_beginthreadex
与CreateThread
的区别:_beginthreadex
相较来说更加的安全,不会产生内存泄漏,在_beginthreadex
的源码中,实际是对CreateThread
进行了封装,所以你可以看到它们的参数几乎是通用的- 语法
uintptr_t _beginthreadex( // NATIVE CODE
void *security,
unsigned stack_size,
unsigned ( __stdcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);
参数解释:
- security:如果该参数为 NULL,则表示不能继承
- stack_size:为 0 时,使用默认的栈大小
- start_address:执行的函数,注意这里的函数类型和
CreateThread
中的就不一样了,要用它的来定义 - arglist:传入要执行的函数的参数的指针
- initflag:为 0 表示线程立即执行
- thrdaddr:线程标识符
- 返回值:线程句柄
该部分代码仅对 CreateThread
中的实例做了很小的修改即可运行
#include
#include
#include /* _beginthread, _endthread */
#define HANDLENUM 5
unsigned __stdcall MyThreadFunction(void *lpParam);
// pass paramter
typedef struct PassValue {
int val1;
int val2;
}PASSVALUE, *PPASSVALUE;
void m_beginthreadex()
{
HANDLE aHandleArray[HANDLENUM];
unsigned int adwThreadID[HANDLENUM]; // receive thread descriptor
// fill paramter
PASSVALUE pv{
1,
2
};
// create threads
for (int i = 0; i < HANDLENUM; i++)
{
aHandleArray[i] = (HANDLE)_beginthreadex(NULL, 0, MyThreadFunction, &pv, 0, &adwThreadID[i]);
}
// omit error handling ...
// wait for all thread execution completed
WaitForMultipleObjects(HANDLENUM, aHandleArray, TRUE, INFINITE);
printf("所有线程执行完毕\n");
// close handle
for (int i = 0; i < HANDLENUM; i++)
{
CloseHandle(aHandleArray[i]);
}
}
unsigned __stdcall MyThreadFunction(void *lpParam)
{
PASSVALUE *pv = (PASSVALUE *)lpParam;
printf("thread id is %d, pass paramter one is: %d, pass paramter two is: %d\n", GetCurrentThreadId(), pv->val1, pv->val2);
return 0;
}
执行结果
thread id is 1920, pass paramter one is: 1, pass paramter two is: 2
thread id is 11760, pass paramter one is: 1, pass paramter two is: 2
thread id is 2896, pass paramter one is: 1, pass paramter two is: 2
thread id is 6556, pass paramter one is: 1, pass paramter two is: 2
thread id is 29496, pass paramter one is: 1, pass paramter two is: 2
所有线程执行完毕
终止线程
有四种终止运行线程的方式
- 线程函数返回,也就是让线程正常执行完毕(推荐)
- 线程通过调用
ExitThread
或者_endthreadex
函数杀死自己,也就是与创建线程对应的函数 - 其他线程(不一定是同一个进程的)调用 TerminateThread 杀死另一个线程
- 进程终止
- 定义:该函数用于等待执行完所有线程才执行接下来的代码语句,MSDN 中的描述语句是,等待指定对象变成有信号(signaled)的状态。或者,函数第二个参数用于设置超时
- 语法
DWORD WaitForMultipleObjects(
[in] DWORD nCount,
[in] const HANDLE *lpHandles,
[in] BOOL bWaitAll,
[in] DWORD dwMilliseconds
);
- nCount:有多少句柄需要等待。结合 CreateThread 中的实例,其实就是要等待的线程的数量
- lpHandles:线程句柄
- bWaitAll:为 TRUE 表示等待所有线程执行完毕。为 FALSE 表示任何一个线程执行完成就返回
- dwMilliseconds:超时设定,如果设置为某个大于 0 的数值,则在该数值到达后,而线程又没有执行完,也依旧继续执行主线程中的剩余代码。若设置为 INFINITE 表示无限等待。
我们将 CreateThread
小节中实例的代码修改如下部分,以演示 bWaitAll
设置为 FLASE
的效果
- 将 HANDLENUM 设置为 50,方便我们更容易观察结果
bWaitAll
设置为FLASE
表示只要有一个线程触发信号则WaitForMultipleObjects
函数执行完成
当 bWaitAll
设置为 TRUE
时,“所有线程执行完毕” 这句话始终出现在输出语句的结尾,而当 bWaitAll
设置为 FLASE
其效果如下
可以看到,该语句将不会在最后出现。
【注】当如果你只用等待一个信号的时候,可以使用 WaitForSingleObject
- 《Windows 核心编程》
- CreateThread function
- Creating Threads
- WaitForMultipleObjects function
- _beginthread, _beginthreadex
- windows 多线程: CreateThread、_beginthread、_beginthreadex、AfxBeginThread 的区别
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)