【一文搞懂】【WIN32】(_beginthread、_beginthreadex)和CreateThread

_beginthreadex和CreateThread

_beginthreadex 和 _beginthread 是C++运行时库的函数

CreateThread是windows系统运行时库的函数

函数介绍

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());  //输出主线程pid
	Sleep(2000);
}

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


终止线程

关于如何有效终止线程可以查看这篇文章👉使用WaitForSingleObject终止线程

《window核心编程》一书中提到,如要终止线程的运行,可以使用下面的方法:

  • 线程函数返回(最好使用这种方法)
  • 通过调用ExitThread函数,线程将自行撤销(但建议最好不要使用这种方法)
  • 同一个进程或另一个进程中的线程调用TerminateThread函数(应该避免使用这种方法)。
  • 包含线程的进程终止运行。(这种方法也应该避免使用)

_beginthreadex_beginthread

C++ 运行期库有两个创建线程的函数,一个是_beginthread,另一个是 _beginthreadex

创建线程函数接口如下:


unsigned long _beginthread(
  void(_cdecl *start_address)(void *), //声明为void (*start_address)(void *)形式 这个参数即为函数名
  unsigned stack_size, //是线程堆栈大小,一般默认为0(表示与主线程使用一样的堆栈)
  void *arglist //向线程传递的参数,一般为结构体,没有参数时设置为NULL
);

//推荐使用
unsigned long _beginthreadex(
  void *security,	//安全属性,NULL表示默认安全性
  unsigned stack_size, //是线程堆栈大小,一般默认为0
  unsigned(_stdcall  *start_address)(void *),	//声明为unsigned(*start_address)(void *)形式
  void *argilist,	//向线程传递的参数,一般为结构体
  unsigned initflag, //新线程的初始状态,0表示立即执行,CREATE_SUSPEND表示创建后挂起。
  unsigned *thrdaddr //该变量存放线程标识符,它是CreateThread函数中的线程ID。
};

线程结束:

//释放线程空间、释放线程TLS空间、调用ExiteThread结束线程。
void _endthread(void); 	

// retval:设定的线程结束码,与ExiteThread函数的参数功能一样,
//其实这个函数释放线程TLS空间,再调用ExiteThread函数,但没有释放线程空间。
void _endthreadex(unsigned retval);

两者的区别在于:

  1. 参数形式不同
  2. ex能够创建悬挂状态线程,在nt中能够指定级别,能够被thrdaddr访问,因为它有了id
  3. ex使用__stdcall调用格式,必须返回exit code
  4. ex返回0代表失败,原先的返回-1L。
  5. ex创建的必须用ex销毁

_beginthreadDemo:

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。

参考:

  • Win32API手册
  • C++手册
  • 网络文章
  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:博客之星2021 设计师:Hiro_C 返回首页
评论

打赏作者

欧恩意

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值