begin和start区别CreateThread()、_beginthreadex()及、AfxBeginThread()函数的讨论
2009-05-30 00:27 1157人阅读 评论(2) 收藏 举报
操作系统中线程是非常重要的概念,所以关于线程的创建常常有些困扰人的内容。好像创建线程的函数很多,那么他们之间的有什么联系与区别呢?正如题目给出的三个函数。今天看了看Windows核心编程,再了一些网上的资料,在此想说说这些函数之间的关系和区别。如有不正确的地方,请各位不吝赐教。
首先,需要说明的是这三个函数都与CreateThread。CreateThread函数是Windows的一个API函数,其具体的使用方法在 MSDN和《Windows核心编程》上都有详细介绍。主要的作用是创建一个线程。_beginthreadex函数是C/C++运行库提供的函数,从 _beginthreadex函数的源代码,可以看出它的主要动作是:增加了一个名为ptd的_ptiddata的结构的处理,然后在调用CreateThread函数。_ptiddata是每个线程都拥有自己的专用的数据结构。关于使用CreateThread代替_beginthreadex的结果以及可能出现的问题在《Windows核心编程》上讲的很清楚: “也许你想知道,如果调用CreateThread,而不是调用C/C++运行期库的_beginthreadex来创建新线程,将会发生什么情况。当一个线程调用要求_ptiddata结构的C /
C + +运行期库函数时,将会发生下面的一些情况(大多数C / C + +运行期库函数都是线程安全函数,不需要该结构)。
首先, C / C + +运行期库函数试图(通过调用T l s G e t Va l u e )获取线程的数据块的地址。如果返回N U L L作为t i d d a t a块的地址,调用线程就不拥有与该地址相关的_t i d d a t a块。这时,C / C + +运行期库函数就在现场为调用线程分配一个_t i d d a t a块,并对它进行初始化。然后该_t i d d a t a块(通过T l s S e t Va l u e)与线程相关联。此时,只要线程在运行,该_t i d d a t a将与线程待在一起。这时,C / C + +运行期库函数就可以使用线程的_t i d d a t a块,而且将来被调用的所有C / C + +运行期函数也能使用_t i d d a t a块。 当然,这看来有些奇怪,因为线程运行时几乎没有任何障碍。不过,实际上还是存在一些问题。首先,如果线程使用C / C + +运行期库的s i g n a l函数,那么整个进程就会终止运行,因为结构化异常处理帧尚未准备好。
第二,如果不是调用_ e n d t h r e a d e x来终止线程的运行,那么数据块就不会被撤消,内存泄漏就会出现(那么谁还为使用C r e a t e T h r e a d函数创建的线程来调用_ e n d t h r e a d e x呢?)。”对于上面所说的两个问题:我也是有疑问的:使用CreateThread创建
线程后,用CloseHandle函数关闭相应的线程句柄,不会对_ptiddata结构进行释放吗? 另外在网上看到一些关于这三个函数的讨论如下: 一直对这三个创建线程的方法都搞不清楚,不知道在什么情况下该用那种方法,下面是从网上摘录的一些帖子:
1、不要在一个MFC程序中使用_beginthreadex()或CreateThread()。这句话的意思是由于AfxBeginThread()是MFC封装的启动线程的函数,里面包含了很多和MFC相关的启动信息,而且封装了一些常用的操作,使用起来也比较简便。而用另外两个函数就需要程序员对类型,安全性检查进行更多的思考!
2、用_beginthreadex()函数应该是最佳选择,因为_beginthreadex()函数是CRun-timeLibrary中的函数,函数的参数和数据类型都是CRun-timeLibrary中的类型,这样在启动线程时就不需要进行Windows数据类型和CRun-timeLibrary中的数据类型之间的转化。减低了线程启动时的资源消耗和时间的消耗!
3、在C程序中,几乎都要用到new和delete,难道只有使用_beginthreadex()?不,因为MFC也是C类库(只不过是Microsoft的C类库,不是标准的C类库),在MFC中也封装了new和delete两中运算符,所以用到new和delete的地方不一定非要使用_beginthreadex()函数,
用其他两个函数都可以!其实在程序中使用上面的哪个函数并不是绝对的,书的作者只不过是提了一个更佳的搭配方法,我在MFC程序中也经常使用_beginthreadex()和CreateThread()这两个函数,运行的效果也没有多大的区别,有的时候只是需要你额外的进行一些类型检查和其他的一些转化操作,其余没有其他不妥! 创建线程只有一个方法是::CreateThread()。_beginthreadex()、AfxBeginThread()等内部都是调用这个函数的,因为操作系统只提供这一个接口C静态库比WINDOWS出来还早,就别提多线程了,所以他对多线程的支持不是很好,但后悔也来不急,但也不能怪人家。
C运行库_beginthreadex()。他经过一些处理后,再调用CreateThread()如果要强制结束的话也最好用_endthreadex结束,因为他也要一些处理。 总结上面的内容,当然《Windows核心编程》上面得说法是比较权威的。所以,在对线程的结构、运行还不是很了解的时候最好还是按照书上的来。这样能够避免一些可能出现的莫名奇妙的错误,也省去的一些其他结构处理的考虑。当你清楚地知道线程的结构与运行机制,以及了解各个函数对CreateThread函数的封装的时候,大概那时候就能够应用自如了。
beginthread beginthreadex CreateThread 区别
(2009-06-19 23:44:20)
转载
标签: thread it | 分类: IT知识 |
建立一个线程。
unsigned long beginthread(void(cdecl *startaddress)(void*),unsigned stacksize, void *arglist);
unsigne dlong beginthreadex(void *security,unsignedstacksize,unsigned(stdcall *startaddress) (void *), void *arglist,unsignedinitflag,unsigned *thrdaddr);
例程 需要的头文件 兼容性
beginthread <process.h> Win NT,Win 95
unsigned long beginthread(void(cdecl *startaddress)(void*),unsigned stacksize, void *arglist);
unsigne dlong beginthreadex(void *security,unsignedstacksize,unsigned(stdcall *startaddress) (void *), void *arglist,unsignedinitflag,unsigned *thrdaddr);
例程 需要的头文件 兼容性
beginthread <process.h> Win NT,Win 95
beginthreadex <process.h> Win NT,Win 95
对于另外兼容性的信息,参见引言中的兼容性
库
LIBC.LIB 单线程静态库,零售版本
LIBCMT.LIB 多线程静态库,零售版本
MSVCRT.LIB MSVCRT.DLL的输入库,零售版本
为了使用beginthread或beginthreadex,该应用必须与多线程C运行库之一进行链接。
返回值
如果成功,这些函数返回最近建立的线程的句柄,出现一个错误时,beginthread返回-1在这种情况下如果有太多的线程,则errno设置为EAGAIN,如果参量无效或栈尺寸不正确,则errno设置为EINVAL。在出现一个错误时,beginthreadex返回0,这种情况下errno和doserrno都被设置。
参数
startaddress
开始执行新线程的例程的起始地址。
对于另外兼容性的信息,参见引言中的兼容性
库
LIBC.LIB 单线程静态库,零售版本
LIBCMT.LIB 多线程静态库,零售版本
MSVCRT.LIB MSVCRT.DLL的输入库,零售版本
为了使用beginthread或beginthreadex,该应用必须与多线程C运行库之一进行链接。
返回值
如果成功,这些函数返回最近建立的线程的句柄,出现一个错误时,beginthread返回-1在这种情况下如果有太多的线程,则errno设置为EAGAIN,如果参量无效或栈尺寸不正确,则errno设置为EINVAL。在出现一个错误时,beginthreadex返回0,这种情况下errno和doserrno都被设置。
参数
startaddress
开始执行新线程的例程的起始地址。
Stacksize
新线程的栈尺寸或0。
Arglist
传递给新线程的参量表或NULL。
Security
新线程的安全指示符;对于Windows 95应用必须为NULL。
Initflag
新线程的初始状态(运行时返回0或暂停时返回CREATESUSPEND)。
Thrdaddr
新函数的地址。
说明
beginthread函数建立一个线程,开始startaddress处例程的执行。在startaddress处的例程必须使用cdecl调用约定且没有返回值,当该线程从这个例程返回时,它自动终止。
.beginthredex比beginthread更紧密地汇编Win32 Create ThreadAPI函数,beginthreadex在如下方面不同于beginthread:
.
新线程的栈尺寸或0。
Arglist
传递给新线程的参量表或NULL。
Security
新线程的安全指示符;对于Windows 95应用必须为NULL。
Initflag
新线程的初始状态(运行时返回0或暂停时返回CREATESUSPEND)。
Thrdaddr
新函数的地址。
说明
beginthread函数建立一个线程,开始startaddress处例程的执行。在startaddress处的例程必须使用cdecl调用约定且没有返回值,当该线程从这个例程返回时,它自动终止。
.beginthredex比beginthread更紧密地汇编Win32 Create ThreadAPI函数,beginthreadex在如下方面不同于beginthread:
.
beginthreadex有另外三个参数:initflag、security和threadaddr。新线程可以在暂停状态中建立,使用指定的安全方式(仅在Windows NT下),可以使用thrdadar访问,它是线程标识符。
.startaddress处的程序传送给beginthreadex,必须使用stdcall调用约定且必须返回一个线程退出码。
失败时beginthreadex返回0,而不是-1。
.eginthreadex建立的线程通过调用endthreadex终止。
你可以显式调用endthread或endthreadex终止一个线程,但当该线程从作为参量传递的例程返回时自动调用endthread或endthreadex。通过调用endthread或endthreadex终止一个线程帮助确保恢复该线程分配的资源。
endthread自动关闭该线程句柄(而endthreadex不这样),因此,当使用beginthread和endthread时,通过调用Win32 CloseHandle API函数并不显式关闭该线程句柄。这个行为不同于Win32 ExitThread API函数。
注意:对于与LIBCMT.LIB链接的可执行文件,不要调用Win32ExitThread API函数;这防止该运行系统要求收回分配的资源。endthread和endthreadex要求收回分配的线程资源,然后调用ExitThread。在beginthread或beginthreadex被调用时,操作系统处理栈的分配你不需要传送
.startaddress处的程序传送给beginthreadex,必须使用stdcall调用约定且必须返回一个线程退出码。
失败时beginthreadex返回0,而不是-1。
.eginthreadex建立的线程通过调用endthreadex终止。
你可以显式调用endthread或endthreadex终止一个线程,但当该线程从作为参量传递的例程返回时自动调用endthread或endthreadex。通过调用endthread或endthreadex终止一个线程帮助确保恢复该线程分配的资源。
endthread自动关闭该线程句柄(而endthreadex不这样),因此,当使用beginthread和endthread时,通过调用Win32 CloseHandle API函数并不显式关闭该线程句柄。这个行为不同于Win32 ExitThread API函数。
注意:对于与LIBCMT.LIB链接的可执行文件,不要调用Win32ExitThread API函数;这防止该运行系统要求收回分配的资源。endthread和endthreadex要求收回分配的线程资源,然后调用ExitThread。在beginthread或beginthreadex被调用时,操作系统处理栈的分配你不需要传送
线程栈地址给这些函数。另外,stacksize参量可以为0,在这种情况下操作系统使用与主线程中指定的栈相同的值。
arglist是传送给最近建立的线程的参数。它通常是一个数据项例如字符串的地址。
arglist如果不需要可以为NULL,但beginthread和beginthreadex必须提供一些传递给新线程的值。如果任何线程调用abort、exit、exit或ExitProcess,则所用线程被终止。
arglist是传送给最近建立的线程的参数。它通常是一个数据项例如字符串的地址。
arglist如果不需要可以为NULL,但beginthread和beginthreadex必须提供一些传递给新线程的值。如果任何线程调用abort、exit、exit或ExitProcess,则所用线程被终止。
在写c++代码时,一直牢记着一句话:决不应该调用CreateThread。相反,应该使用Visual C++运行期库函数_beginthreadex。
好像CreateThread函数就是老虎,既然这样为什么微软要开发这个函数呢?
从网上到的相关资料,现在汇总一下,在此对相关人员进行感谢!
摘自《windows 核心编程》:
CreateThread函数是用来创建线程的Windows函数。不过,如果你正在编写C/C++代码,决不应该调用CreateThread。相反,应该使用Visual C++运行期库函数_beginthreadex。如果不使用Microsoft的Visual C++编译器,你的编译器供应商有它自己的CreateThred替
代函数。
若要使多线程C和C++程序能够正确地运行,必须创建一个数据结构,并将它与使用C/C++运行期库函数的每个线程关联起来。当你调用C/C++运行期库时,这些函数必须知道查看调用线程的数据块,这样就不会对别的线程产生不良影响。
1.每个线程均获得由C/C++运行期库的堆栈分配的自己的tiddata内存结构。
2.传递给_beginthreadex的线程函数的地址保存在tiddata内存块中。传递给该函数的参数也保存在该数据块中。
3._beginthreadex确实从内部调用CreateThread,因为这是操作系统了解如何创建新线程的唯一方法。
4.当调用CreatetThread时,它被告知通过调用_threadstartex而不是pfnStartAddr来启动执行新线程。 还有,传递给线程函数的参数是tiddata结构而不是pvParam的地址。
5.如果一切顺利,就会像CreateThread那样返回线程句柄。如果任何操作失败了,便返回NULL。
_beginthreadex和_beginthread函数的区别。_beginthread函数的参数比较少,因此比特性全面的_beginthreadex函数受到更大的限制。
例如,如果使用_beginthread,就无法创建带有安全属性的新线程,无法创建暂停的线程,也无法获得线程的ID值。
下面摘录Csdn中的Holly()的帖子进行解释,再次表示感谢。
来源:topic.csdn/t/20000926/10/31810.html
Holly():
oldworm提供了很好的使用的例子,而且也运用了编译控制!
我来解释一下理论上的区别:
CreateThread、_beginthread和_beginthreadex都是用来启动线程的,但大家看到oldworm没有提供_beginthread的方式,原因简单,_beginthread是_beginthreadex的功能子集,虽然_beginthread内部是调用_beginthreadex但他屏蔽了象安全特性这样的功能,所以_begint
hread与CreateThread不是同等级别,_beginthreadex和CreateThread在功能上完全可替代,我们就来比较一下_beginthreadex与CreateThread!
CRT的函数库在线程出现之前就已经存在,所以原有的CRT不能真正支持线程,这导致我们在编程的时候有了CRT库的选择,在MSDN中查阅CRT的函数时都有:
Libraries
LIBC.LIB Single thread static library, retail version
LIBCMT.LIB Multithread static library, retail version
MSVCRT.LIB Import library for MSVCRT.DLL, retail version
LIBC.LIB Single thread static library, retail version
LIBCMT.LIB Multithread static library, retail version
MSVCRT.LIB Import library for MSVCRT.DLL, retail version
这样的提示!
对于线程的支持是后来的事!
这也导致了许多CRT的函数在多线程的情况下必须有特殊的支持,不能简单的使用CreateThread就OK。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论