在写
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 的地址。
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
()的帖子进行解释,再次表示感谢。
Holly
():
oldworm
提供了很好的使用的例子,而且也运用了编译控制!
我来解释一下理论上的区别:
CreateThread 、 _beginthread 和 _beginthreadex 都是用来启动线程的,但大家看到 oldworm 没有提供 _beginthread 的方式,原因简单, _beginthread 是 _beginthreadex 的功能子集,虽然 _beginthread 内部是调用 _beginthreadex 但他屏蔽了象安全特性这样的功能,所以 _beginthread 与 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
这样的提示!
对于线程的支持是后来的事!
这也导致了许多 CRT 的函数在多线程的情况下必须有特殊的支持,不能简单的使用 CreateThread 就 OK 。
大多的 CRT 函数都可以在 CreateThread 线程中使用,看资料说只有 signal() 函数不可以,会导致进程终止!但可以用并不是说没有问题!
有些 CRT 的函数象 malloc(), fopen(), _open(), strtok(), ctime(), 或 localtime() 等函数需要专门的线程局部存储的数据块,这个数据块通常需要在创建线程的时候就建立,如果使用 CreateThread ,这个数据块就没有建立,然后会怎样呢?在这样的线程中还是可以使用这些函数而且没有出错,实际上函数发现这个数据块的指针为空时,会自己建立一个,然后将其与线程联系在一起,这意味着如果你用 CreateThread 来创建线程,然后使用这样的函数,会有一块内存在不知不觉中创建,遗憾的是,这些函数并不将其删除,而 CreateThread 和 ExitThread 也无法知道这件事,于是就会有 Memory Leak ,在线程频繁启动的软件中 ( 比如某些服务器软件 ) ,迟早会让系统的内存资源耗尽!
_beginthreadex( 内部也调用 CreateThread) 和 _endthreadex 就对这个内存块做了处理,所以没有问题! ( 不会有人故意用 CreateThread 创建然后用 _endthreadex 终止吧,而且线程的终止最好不要显式的调用终止函数,自然退出最好! )
谈到 Handle 的问题, _beginthread 的对应函数 _endthread 自动的调用了 CloseHandle ,而 _beginthreadex 的对应函数 _endthreadex 则没有,所以 CloseHandle 无论如何都是要调用的不过 _endthread 可以帮你执行自己不必写,其他两种就需要自己写! (Jeffrey Richter 强烈推荐尽量不用显式的终止函数,用自然退出的方式,自然退出当然就一定要自己写 CloseHandle)
我来解释一下理论上的区别:
CreateThread 、 _beginthread 和 _beginthreadex 都是用来启动线程的,但大家看到 oldworm 没有提供 _beginthread 的方式,原因简单, _beginthread 是 _beginthreadex 的功能子集,虽然 _beginthread 内部是调用 _beginthreadex 但他屏蔽了象安全特性这样的功能,所以 _beginthread 与 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
这样的提示!
对于线程的支持是后来的事!
这也导致了许多 CRT 的函数在多线程的情况下必须有特殊的支持,不能简单的使用 CreateThread 就 OK 。
大多的 CRT 函数都可以在 CreateThread 线程中使用,看资料说只有 signal() 函数不可以,会导致进程终止!但可以用并不是说没有问题!
有些 CRT 的函数象 malloc(), fopen(), _open(), strtok(), ctime(), 或 localtime() 等函数需要专门的线程局部存储的数据块,这个数据块通常需要在创建线程的时候就建立,如果使用 CreateThread ,这个数据块就没有建立,然后会怎样呢?在这样的线程中还是可以使用这些函数而且没有出错,实际上函数发现这个数据块的指针为空时,会自己建立一个,然后将其与线程联系在一起,这意味着如果你用 CreateThread 来创建线程,然后使用这样的函数,会有一块内存在不知不觉中创建,遗憾的是,这些函数并不将其删除,而 CreateThread 和 ExitThread 也无法知道这件事,于是就会有 Memory Leak ,在线程频繁启动的软件中 ( 比如某些服务器软件 ) ,迟早会让系统的内存资源耗尽!
_beginthreadex( 内部也调用 CreateThread) 和 _endthreadex 就对这个内存块做了处理,所以没有问题! ( 不会有人故意用 CreateThread 创建然后用 _endthreadex 终止吧,而且线程的终止最好不要显式的调用终止函数,自然退出最好! )
谈到 Handle 的问题, _beginthread 的对应函数 _endthread 自动的调用了 CloseHandle ,而 _beginthreadex 的对应函数 _endthreadex 则没有,所以 CloseHandle 无论如何都是要调用的不过 _endthread 可以帮你执行自己不必写,其他两种就需要自己写! (Jeffrey Richter 强烈推荐尽量不用显式的终止函数,用自然退出的方式,自然退出当然就一定要自己写 CloseHandle)
转载于:https://blog.51cto.com/338558/76160