C++多线程系列(一)CreateThread和_beginthreadex区别

现在在学习多线程,顺便将搜集到的资料整理下来以供参考和查询。

首先在开始多线程学习的时候遇到的首要问题便是多线程的创建,在查阅资料后有CreateThread和_beginthreadex两种方法,可能不止这两种,以后学习到了再补充。


----------------------------------------------20160914更新-------------------------------------------------

在创建新的线程时一定要使用_beginthreadex函数而不要使用CreateThread函数,因为CreateThread函数对系统中的全局变量没有保护,所以多个线程环境下容易出现系统的全局变量的值被覆盖的情况,而_beginthreadex每个线程都有单独的系统全局变量入errno.

----------------------------------------------------------------------------------------------------------------------------------------------------------


(1)CreateThread

HANDLE
WINAPI
CreateThread(
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,		//线程的安全属性,NULL则为默认属性
    _In_ SIZE_T dwStackSize,								//指定线程堆栈大小,NULL则默认堆栈大小
    _In_ LPTHREAD_START_ROUTINE lpStartAddress,				//线程函数其实地址及执行线程的函数
    _In_opt_ __drv_aliasesMem LPVOID lpParameter,			//传递给线程函数的参数
    _In_ DWORD dwCreationFlags,								//指定线程创建后是否立即执行,若0则表示线程立即执行
    _Out_opt_ LPDWORD lpThreadId
    );


lpThreadAttributes,是一个执行SECURITY_ATTRIBUTES结构的指针,如果需要默认安全属性则传递参数NULL,
 如果希望此线程对象句柄可以被子进程继承的话,必须设定一个SECURITY_ATTRIBUTES的机构,将它的bInheritHandle成员初始化为TRUE

如下:

SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.IpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;     //使CreateThread返回的句柄可以被继承

HANDLE handle = CreateThread(&sa,...);


dwCreationFlags,创建标志,如果是0表示线程被创建后立即开始运行,如果指定为CREATE_SUSPENDED标志,表示线程被创建以后处于挂
 起状态,直到使用ResumeThread显式启动线程为止。

#include 
#include 
//子线程函数
DWORD WINAPI ThreadFun(LPVOID pM)
{
	printf("子线程的线程ID号为:%d\n子线程输出Hello World\n", GetCurrentThreadId());
	return 0;
}
//主函数,所谓主函数其实就是主线程执行的函数。
int main()
{
	HANDLE handle = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL);
	WaitForSingleObject(handle, INFINITE);
	return 0;
}

其中DWORD:unsigned long; WINAPI:_stdcall; LPVOID:void *


(2)_beginthreadex函数

在C/C++标准运行库里面有许多的全局变量入errno、strerror等,他们可以用来表示线程当前的状态。但是在多线程设计中每个线程必须有唯一的状态,佛则这些变量记录的信息准确度无法保证。如下代码所示

if (system("notepad.exe readme.txt") == -1)
	{
		switch(errno)
		{
			....//程序处理错误代码
		}
	}


加入某个线程A执行上面的代码,在执行system后,还未来得及调用switch()函数,这时线程B同样执行该代码出错,并将错误代号写入全局变量errno中,这样,A一旦开始执行swtich()时,它所访问的代码并不是本身错误代码而是线程B赋予的错误代码。像strerror()、strtok()、tmpnam()、gmtime()、asctime()等函数也会遇到这种由于多个线程访问修改导致的数据覆盖问题。

为了避免此类情况发生,Windows操作系统提供了标准运行库函数 _beginthreadex,让运行期设置了相关变量后再去调用Windows系统提供的CreateThread函数。_beginthreadex的参数与CreateThread函数是对应的,只是参数名和类型完全不同,使用的时候需要进行强制转化。_beginthreadex函数定义如下:

_ACRTIMP uintptr_t __cdecl _beginthreadex(
    _In_opt_  void*                    _Security,
    _In_      unsigned                 _StackSize,
    _In_      _beginthreadex_proc_type _StartAddress,
    _In_opt_  void*                    _ArgList,
    _In_      unsigned                 _InitFlag,
    _Out_opt_ unsigned*                _ThrdAddr
    );


参数含义与CreateThread函数是对应的。实例代码如下:
#include 
#include 
#include 
//子线程函数
unsigned int __stdcall ThreadFun(PVOID pM)
{
	printf("线程ID号为%4d的子线程说:Hello World\n", GetCurrentThreadId());
	return 0;
}
//主函数,所谓主函数其实就是主线程执行的函数。
int main()
{
	const int THREAD_NUM = 5;
	HANDLE handle[THREAD_NUM];
	for (int i = 0; i < THREAD_NUM; i++)
		handle[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);
	WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
	return 0;
}

本博文参考博客: http://blog.csdn.net/morewindows/article/details/7421759
参考教材:《Windows程序设计第三版》-张铮


你可能感兴趣的:(C\C++)