秒杀多线程第二篇 多线程第一次亲密接触 CreateThread与

本文将带领你与多线程作第一次亲密接触,并深入分析CreateThread与_beginthreadex的本质区别,相信阅读本文后你能轻松的使用多线程并能流畅准确的回答CreateThread与_beginthreadex到底有什么区别,在实际的编程中到底应该使用CreateThread还是_beginthreadex?

使用多线程其实是非常容易的,下面这个程序的主线程会创建了一个子线程并等待其运行完毕,子线程就输出它的线程ID号然后输出一句经典名言——Hello World。整个程序的代码非常简短,只有区区几行。

//最简单的创建多线程实例#include <stdio.h>#include <windows.h>//子线程函数DWORD WINAPI ThreadFun(LPVOID pM){printf("子线程的线程ID号为:%d\n子线程输出Hello World\n", GetCurrentThreadId());return 0;}//主函数,所谓主函数其实就是主线程执行的函数。int main(){printf("最简单的创建多线程实例\n");printf(" — by MoreWindows( ) –\n\n");HANDLE handle = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL);WaitForSingleObject(handle, INFINITE);return 0;}

);

函数说明:

第一个参数为要等待的内核对象。

第二个参数为最长等待的时间,以毫秒为单位,如传入5000就表示5秒,传入0就立即返回,传入INFINITE表示无限等待。

因为线程的句柄在线程运行时是未触发的,线程结束运行,句柄处于触发状态。所以可以用WaitForSingleObject()来等待一个线程结束运行。

函数返回值:

在指定的时间内对象被触发,函数返回WAIT_OBJECT_0。超过最长等待时间对象仍未被触发返回WAIT_TIMEOUT。传入参数有错误将返回WAIT_FAILED

CreateThread()函数是Windows提供的API接口,在C/C++语言另有一个创建线程的函数_beginthreadex(),在很多书上(包括《Windows核心编程》)提到过尽量使用_beginthreadex()来代替使用CreateThread(),这是为什么了?下面就来探索与发现它们的区别吧。

首先要从标准C运行库与多线程的矛盾说起,标准C运行库在1970年被实现了,由于当时没任何一个操作系统提供对多线程的支持。因此编写标准C运行库的程序员根本没考虑多线程程序使用标准C运行库的情况。比如标准C运行库的全局变量errno。很多运行库中的函数在出错时会将错误代号赋值给这个全局变量,这样可以方便调试。但如果有这样的一个代码片段:

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

等函数也会遇到这种由多个线程访问修改导致的数据覆盖问题。

为了解决这个问题,Windows操作系统提供了这样的一种解决方案——每个线程都将拥有自己专用的一块内存区域来供标准C运行库中所有有需要的函数使用。而且这块内存区域的创建就是由C/C++运行库函数_beginthreadex()来负责的。下面列出_beginthreadex()函数的源代码(我在这份代码中增加了一些注释)以便读者更好的理解_beginthreadex()函数与CreateThread()函数的区别。

//_beginthreadex源码整理By MoreWindows( )_MCRTIMP uintptr_t __cdecl _beginthreadex(void *security,unsigned stacksize,unsigned (__CLR_OR_STD_CALL * initialcode) (void *),void * argument,unsigned createflag,unsigned *thrdaddr){_ptiddata ptd;//pointer to per-thread data 见注1uintptr_t thdl;//thread handle 线程句柄unsigned long err = 0L; //Return from GetLastError()unsigned dummyid; //dummy returned thread ID 线程ID号// validation section 检查initialcode是否为NULL_VALIDATE_RETURN(initialcode != NULL, EINVAL, 0);//Initialize FlsGetValue function pointer__set_flsgetvalue();//Allocate and initialize a per-thread data structure for the to-be-created thread.//相当于new一个_tiddata结构,并赋给_ptiddata指针。if ( (ptd = (_ptiddata)_calloc_crt(1, sizeof(struct _tiddata))) == NULL )goto error_return;// Initialize the per-thread data//初始化线程的_tiddata块即CRT数据区域 见注2_initptd(ptd, _getptd()->ptlocinfo);//设置_tiddata结构中的其它数据,这样这块_tiddata块就与线程联系在一起了。ptd->_initaddr = (void *) initialcode; //线程函数地址ptd->_initarg = argument;//传入的线程参数ptd->_thandle = (uintptr_t)(-1);#if defined (_M_CEE) || defined (MRTDLL)if(!_getdomain(&(ptd->__initDomain))) //见注3{goto error_return;}#endif // defined (_M_CEE) || defined (MRTDLL)// Make sure non-NULL thrdaddr is passed to CreateThreadif ( thrdaddr == NULL )//判断是否需要返回线程ID号thrdaddr = &dummyid;// Create the new thread using the parameters supplied by the caller.//_beginthreadex()最终还是会调用CreateThread()来向系统申请创建线程if ( (thdl = (uintptr_t)CreateThread((LPSECURITY_ATTRIBUTES)security,stacksize,_threadstartex,(LPVOID)ptd,createflag,(LPDWORD)thrdaddr))== (uintptr_t)0 ){err = GetLastError();goto error_return;}//Good returnreturn(thdl); //线程创建成功,返回新线程的句柄.//Error returnerror_return://Either ptd is NULL, or it points to the no-longer-necessary block//calloc-ed for the _tiddata struct which should now be freed up.//回收由_calloc_crt()申请的_tiddata块_free_crt(ptd);// Map the error, if necessary.// Note: this routine returns 0 for failure, just like the Win32// API CreateThread, but _beginthread() returns -1 for failure.//校正错误代号(可以调用GetLastError()得到错误代号)if ( err != 0L )_dosmaperr(err);return( (uintptr_t)0 ); //返回值为NULL的效句柄}

c文件中

当你感到悲哀痛苦时,最好是去学些什么东西。

秒杀多线程第二篇 多线程第一次亲密接触 CreateThread与

相关文章:

你感兴趣的文章:

标签云: