网址:
http://www.soft-bin.com/html/2010/08/03/createthread_and_beginthreadex.html
在使用VS创建一个工程时,我们可以选择使用的run-time library,在run-time libary的下拉菜单中,有 Single-Threaded, Multithreaded, MultiThreaded DLL等好几种选项,不同的选项会为我们链接不同的C++ rum-time库。
之所以run-time库会有单线程和多线程的区别,是因为标准C运行库在最初设计时,并没有考虑到其将被运用于多线程的应用程序,这将会引起一些问题。
例如对于C运行期的全局变量errno,我们在多线程环境下,不能保证在设置errno和获取errno之间,没有其他线程对这个全局变量进行了设置,这就会产生一定的问题。而要解决这个问题,我们可以使用线程专属全局变量 ,这个概念我将在以后讲到,但在这个地方,C++ run-time使用的方式是在创建新线程时,为每一个线程分配一个数据块,来保存这些全局的信息。
在Windows下创建线程调用的是Windows的API函数CreateThread,但CreateThread只是一个与操作系统相关的函数,负责在操作系统内部创建线程内核对象,为线程分配栈空间。为线程的运行准备适宜的条件,并不是操作系统应该考虑的事情。比如说,C++ run-time需要在创建线程时,创建一个线程专属的errno变量,但可能其他的运行环境又没有这一项要求,我们不能强制要求Windows操作系统去迎合C++ run-time的需求。
CreateThread并不能正确地初始化C++ run-time对于多线程环境的要求,因此C++ run-time提供了自己的线程创建函数 _beginthreadex,其函数原型如下:
unsigned long _beginthreadex(
|
unsigned (*start_address)( void *),
|
这个函数的参数和CreateThread的参数基本一致,不同的是参数类型不同。这是因为C++ run-time并不从属于操作系统,不应当对操作系统的数据类型有任何依赖。因此我们可以看到 _beginthreadex 函数的参数完全是基本的C++数据类型。
_beginthreadex 函数将会创建线程专属的数据块,并对其进行初始化,而后调用操作系统的线程创建函数以创建一个线程,其伪码如下:
unsigned long _beginthreadex(
|
unsigned (*start_address)( void *),
|
_ptiddata ptd; // 指向线程专属数据块的指针,这个结构不作详细介绍,只需知道其中包含线程专属全局变量
|
unsigned long thdl; // 线程句柄
|
ptd = _calloc_crt(1, sizeof ( struct tiddata));
|
ptd->_initaddr = ( void *)pfnStartAddr; // 保存线程函数地址
|
ptd->_initarg = pvParam; // 保存线程函数的参数
|
thdl = (unsigned long ) CreateThread(pas, cbStack, _threadstartex, ( PVOID )ptd, fdwCreate, pdwThreadID);
|
我们可以看到,_beginthreadex首先创建了一个线程专属数据块,这个数据块中自然就保存了errno之类的线程专属数据,至于如何获取和设置这些数据,稍后我会提到。 创建线程仍然是调用的Windows API函数CreateThread,因为线程的创建与操作系统息息相关,我们唯有通过其API函数CreateThread才可以创建之。
_beginthreadex 将线程的入口函数变更为了 _threadstartex,我们可以看其伪码:
static unsigned long WINAPI _threadstartex( void * ptd)
|
TlsSetValue(__tlsidnex, ptd);
|
((_ptiddata) ptd)->_tid = GetCurrentThreadId();
|
unsigned ret = ptd->_initaddr(ptd->_initarg);
|
_exit(GetExceptionCode());
|
我们可以看到 _threadstartex 仍然是调用了 _beginthreadex 函数中最初传入的线程函数,不同的是,在调用线程函数之前,我们使用了异常捕获,防止线程函数崩溃导致程序崩溃。在线程函数执行完毕后,_threadstartex会紧接着调用 _endthreadex函数,这个函数的作用有两点:
1. 释放线程专属数据块,避免内存泄漏
2. 调用ExitThread退出线程,将退出码写入到线程内核对象中
至此我们可以知道,CreateThread和_beginthreadex两个函数的区别在于:
CreateThread是操作系统提供的创建线程的API,_beginthreadex是C++ run-time提供的用于对线程进行初始化,创建线程转悠数据块的函数。其内部创建线程的操作,仍然通过CreateThread来实现。
我们在编写C++程序创建线程时,应当使用_beginthreadex,而绝不要使用CreateThread,否则会引起错误。
在我们使用_beginthreadex函数时,必须选择run-time链接选项为多线程库,否则会出现找不到_beginthreadex的错误。
我们之前提到过errno这个全局变量,在run-time为多线程库的时候,它实际上是一个宏:
#if defined(_MT) || defined(_DLL)
|
extern int * __cdecl _errno( void );
|
#define errno (*_errno())
|
我们在选择多线程库后,使用errno时,实际上是调用了多线程库中的 _errno()函数,这个函数会将线程专属数据块中的errno值获取出来。
可以注意到,_errno() 实际上是取得了线程专属数据块中errno的地址,因此我们也可以对errno进行设置,其行为就如同直接使用一个int类型变量一样。
CreateThread和_beginthreadex的区别相关推荐
- 多线程之 CreateThread与_beginthreadex本质区别
本文将带领你与多线程作第一次亲密接触,并深入分析CreateThread与_beginthreadex的本质区别,相信阅读本文后你能轻松的使用多线程并能流畅准确的回答CreateThread与_beg ...
- 【转】windows多线程CreateThread与_beginthreadex本质区别
本文将带领你与多线程作第一次亲密接触,并深入分析CreateThread与_beginthreadex的本质区别,相信阅读本文后你能轻松的使用多线程并能流畅准确的回答CreateThread与_beg ...
- 秒杀多线程第二篇 多线程第一次亲密接触 CreateThread与_beginthreadex本质区别
本文将带领你与多线程作第一次亲密接触,并深入分析CreateThread与_beginthreadex的本质区别,相信阅读本文后你能轻松的使用多线程并能流畅准确的回答CreateThread与_beg ...
- 多线程 CreateThread与_beginthreadex本质区别
本文参考了 http://blog.csdn.net/morewindows/article/details/7421759 主函数创建一个线程,并且等待它执行完毕 #include <iost ...
- CreateThread与_beginthreadex本质区别
原文地址:http://blog.csdn.net/morewindows/article/details/7421759 使用多线程其实是非常容易的,下面这个程序的主线程会创建了一个子线程并等待其运 ...
- CreateThread 和_beginthreadex区别
本文将带领你与多线程作第一次亲密接触,并深入分析CreateThread与_beginthreadex的本质区别,相信阅读本文后你能轻松的使用多线程并能流畅准确的回答CreateThread与_beg ...
- windows线程之CreateThread与_beginthreadex区别详解
CreateThread函数 CreateThread函数是用于创建线程的Windows函数. CreateThread函数参数介绍 HANDLE CreateThread(LPSECURITY_AT ...
- CreateThread、_beginthreadex和AfxBeginThread 的区别
CreateThread._beginthreadex和AfxBeginThread 创建线程好几个函数可以使用,可是它们有什么区别,适用于什么情况呢? 参考了一些资料,写得都挺好的,这里做一些摘抄和 ...
- CreateThread、_beginthreadex、AfxBeginThread
1.CreateThread._beginthreadex.AfxBeginThread的区别和正确使用: CreateThread是一个Windows的API函数,_beginthreadex是一个 ...
最新文章
- 较深度地递归转义过滤
- [实验]自举?只不过是电容和二极管捣的乱
- java.lang.IllegalArgumentException at org.springframework.asm.ClassReader.init(Unknown Source)
- MONTH_NAMES_GET
- 循环嵌套-[扩展]print函数的结尾处理
- Delphi调用外部程序的集中方法
- python安装不上怎么办_python安装不了怎么办
- java分页 jar_零基础学java之javaEE,分页
- mysql 数据库编码怎么看,查看数据库编码_查看mysql编码方式 mysql数据库编码查看方法...
- linux-Centos 7下bond与vlan技术的结合[推荐]
- Python3.x:pytesseract识别率提高(样本训练)
- 【R语言】如何直接调取Wind、iFinD数据接口教程
- 用C#制作PDF文件全攻略
- python自动填表单_用python-webdriver实现自动填表
- 微软官方的SQLHelper类(含完整中文注释)
- vue.js 密码加密_1Password ——密码管理工具
- IE8 “Automation 服务器不能创建对象”问题解决方法
- 「敏捷」Stacey Matrix模型帮你确定合适的项目管理方法
- Time Series FeatuRe Extraction on basis of Scalable Hypothesis tests (tsfresh –A Python package)
- 如何用WGDI进行共线性分析(一点五)
热门文章
- 常用宏定义 - 系统相关
- young people can also be a leader
- 读书笔记《集体智慧编程》Chapter 5 : Optimization
- 什么MySQL语句在存储过程体中是合法的
- 多处理机的进程调度方式
- Systemd 入门教程之命令篇
- Android中创建自己的对话框
- ubuntu进入桌面自动启动脚本_在 Ubuntu 下开机自启动自己的 QT 程序而不启动 Ubuntu 的桌面...
- opencv python教程简书_OpenCV-Python系列二:常用的图像属性
- Ubuntu 键盘错位解决 更改键盘布局