_beginthreadex和CreatThread
函数介绍
CreateThread
先从比较常见的CreatThread 说起,CreateThread函数创建一个在调用进程的地址空间内执行的线程。
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES 【lpThreadAttributes】,
DWORD 【dwStackSize】,
LPTHREAD_START_ROUTINE 【lpStartAddress】,
LPVOID 【lpParameter参数】,
DWORD 【dwCreationFlags】,
LPDWORD 【lpThreadId】
);
Win32API参考手册
返回值
如果函数成功,则返回值是新线程的句柄。
如果函数失败,返回值为NULL。要获取扩展错误信息,请调用GetLastError.
CreateThread Demo:
#include<Windows.h>
#include<stdio.h>
DWORD WINAPI ThreadFunc(LPVOID);
void main()
{
HANDLE hThread;
DWORD threadId;
hThread = CreateThread(NULL, 0, ThreadFunc, 0, 0, &threadId);
printf("我是主线程, pid = %d\n", GetCurrentThreadId());
Sleep(2000);
}
DWORD WINAPI ThreadFunc(LPVOID p)
{
printf("我是子线程, pid = %d\n", GetCurrentThreadId());
return 0;
}
终止线程
《window核心编程》一书中提到,如要终止线程的运行,可以使用下面的方法:
- 线程函数返回(最好使用这种方法)
- 通过调用
ExitThread 哈桑农户,线程将自行撤销(最好不要使用这种方法) - 同一个进程或另一个进程中的线程调用TerminateThread函数(应该避免使用这种方法)。
- 包含线程的进程终止运行。
_beginthreadex 和 _beginthread
C++ 运行期库有两个创建线程的函数,一个是_beginthread ,另一个是 _beginthreadex 。
创建线程函数接口如下:
unsigned long _beginthread(
void(_cdecl *start_address)(void *),
unsigned stack_size,
void *arglist
);
unsigned long _beginthreadex(
void *security,
unsigned stack_size,
unsigned(_stdcall *start_address)(void *),
void *argilist,
unsigned initflag,
unsigned *thrdaddr
};
线程结束:
void _endthread(void);
void _endthreadex(unsigned retval);
两者的区别在于:
- 参数形式不同
ex 能够创建悬挂状态线程,在nt中能够指定级别,能够被thrdaddr 访问,因为它有了id 。ex 使用__stdcall 调用格式,必须返回exit code 。ex 返回0代表失败,原先的返回-1L。ex 创建的必须用ex销毁。
_beginthread Demo:
程序最后的system("pause") 必须有,因为_begeinThread 调用时,参数2的值为0 ,即子线程是挂靠在主线程的,当主线程结束时,子线程也被迫结束,由于主线程执行时间过于短暂,因此需要使用该语句控制线程处于暂停状态,另外,由于三个线程的问题,为了控制打印的次序,在线程中也加了Sleep() 函数控制执行的时刻。
例子也包括了_begeinThread 如何传参。
#include <iostream>
#include <windows.h>
#include <process.h>
#include <string.h>
using std::cout;
using std::endl;
typedef struct _STU_PEOPLE
{
char szName[50];
int age;
}STU_PEOPLE,*PSTU_PEOPLE;
void thread_1(void *)
{
cout << "调用了线程 thread_1 无进入参数" << endl;
}
void thread_2(void *lpVoid)
{
Sleep(10);
int pnInt = (int)lpVoid;
cout << "调用了线程 thread_2 进入参数为:" << pnInt << endl;
}
void thread_3(void *lpVoid)
{
Sleep(50);
PSTU_PEOPLE pnInt = (PSTU_PEOPLE)lpVoid;
cout << "调用了线程 thread_3 进入参数为:" << pnInt->szName << ':' << pnInt->age << endl;
}
int main()
{
STU_PEOPLE stuChild;
char name[] = { "Child_LI" };
strcpy_s(stuChild.szName, name);
stuChild.age = 6;
std::cout << "Start Thread!\n";
_beginthread(thread_1, 0, NULL);
_beginthread(thread_2, 0, (void*)stuChild.age);
_beginthread(thread_3, 0, (void*)&stuChild);
std::cout << "End Thread!\n";
Sleep(100);
system("pause");
}
输出:
需要注意的是,Sleep() 函数并不能保证线程严格按照规定的时间运行。
1. 当我们用_beginthread() 函数创建一个线程之后,这个线程将马上伺机执行,但是需要等待CPU为其分配资源,线程执行的顺序是不一定的(完全有可能最先创建的线程最后执行);
2. main函数是主线程函数,在main函数中创建的线程为子线程。在主线程结束后子线程将被迫停止,因此子线程实际上不会被执行;
3. 为了执行子线程,可以将主线程一直运行,如while(1); 或者system("pause") ; 因此在写项目代码时,一定不要忘记在线程的函数中添加while(1) 循环
也可以将某个线程强制休眠,用Sleep() 函数。Sleep() 函数将指定某个线程至少休眠多长时间。
区分
在 Win32 API 中,创建线程的基本函数是 CreateThread ,而 _beginthread(ex) 是 C++ 运行库的函数。为什么要有两个呢?
因为C++ 运行库里面有一些函数使用了全局 量,如果使用 CreateThread 的情况下使用这些C++ 运行库的函数,就会出现不安全 的问题。而 _beginthreadex 为这些全局变量做了处理,使得每个线程都有一份独立 的“全局”量。
所以,如果你的编程只调用 Win32 API/SDK ,就放心用 CreateThread ;如果要用到 C++ 运行时间库,那么就要使用_beginthreadex ,并且需要在编译环境中选择 Use MultiThread Lib/DLL。
参考:
|