网址:

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(
    void* security,
    unsigned stack_size,
    unsigned (*start_address)(void*),
    void* arglist,
    unsigned initflag,
    unsigned* thrdaddr);

这个函数的参数和CreateThread的参数基本一致,不同的是参数类型不同。这是因为C++ run-time并不从属于操作系统,不应当对操作系统的数据类型有任何依赖。因此我们可以看到 _beginthreadex 函数的参数完全是基本的C++数据类型。

_beginthreadex 函数将会创建线程专属的数据块,并对其进行初始化,而后调用操作系统的线程创建函数以创建一个线程,其伪码如下:

unsigned long _beginthreadex(
    void* security,
    unsigned stack_size,
    unsigned (*start_address)(void*),
    void* arglist,
    unsigned initflag,
    unsigned* thrdaddr)
{
    _ptiddata ptd;  // 指向线程专属数据块的指针,这个结构不作详细介绍,只需知道其中包含线程专属全局变量
    unsigned long thdl;   // 线程句柄
 
    // 申请线程专属数据块
    ptd = _calloc_crt(1, sizeof(struct tiddata));
    initptd(ptd);   // 初始化
 
    // 保存一些需要的信息
    ptd->_initaddr = (void*)pfnStartAddr;    // 保存线程函数地址
    ptd->_initarg = pvParam;   // 保存线程函数的参数
 
     // 创建线程
     thdl = (unsigned long) CreateThread(pas, cbStack, _threadstartex, (PVOID)ptd, fdwCreate, pdwThreadID);
 
     if (thdl == NULL)
     {
           _free_crt(ptd);
           return 0;
     }
      return thdl;
}

我们可以看到,_beginthreadex首先创建了一个线程专属数据块,这个数据块中自然就保存了errno之类的线程专属数据,至于如何获取和设置这些数据,稍后我会提到。 创建线程仍然是调用的Windows API函数CreateThread,因为线程的创建与操作系统息息相关,我们唯有通过其API函数CreateThread才可以创建之。

_beginthreadex 将线程的入口函数变更为了 _threadstartex,我们可以看其伪码:

static unsigned long WINAPI _threadstartex(void* ptd)
{
    // 保存ptd,我们在其他地方会再获取ptd
    TlsSetValue(__tlsidnex, ptd); 
 
    ((_ptiddata) ptd)->_tid = GetCurrentThreadId();
 
    __try
    {
        unsigned ret = ptd->_initaddr(ptd->_initarg);
        _endthreadex(ret);
    }
    __except()
    {
        _exit(GetExceptionCode());
    }
    return 0;
}

我们可以看到 _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())
#else
    extern int errno;
#endif

我们在选择多线程库后,使用errno时,实际上是调用了多线程库中的 _errno()函数,这个函数会将线程专属数据块中的errno值获取出来。

可以注意到,_errno() 实际上是取得了线程专属数据块中errno的地址,因此我们也可以对errno进行设置,其行为就如同直接使用一个int类型变量一样。

CreateThread和_beginthreadex的区别相关推荐

  1. 多线程之 CreateThread与_beginthreadex本质区别

    本文将带领你与多线程作第一次亲密接触,并深入分析CreateThread与_beginthreadex的本质区别,相信阅读本文后你能轻松的使用多线程并能流畅准确的回答CreateThread与_beg ...

  2. 【转】windows多线程CreateThread与_beginthreadex本质区别

    本文将带领你与多线程作第一次亲密接触,并深入分析CreateThread与_beginthreadex的本质区别,相信阅读本文后你能轻松的使用多线程并能流畅准确的回答CreateThread与_beg ...

  3. 秒杀多线程第二篇 多线程第一次亲密接触 CreateThread与_beginthreadex本质区别

    本文将带领你与多线程作第一次亲密接触,并深入分析CreateThread与_beginthreadex的本质区别,相信阅读本文后你能轻松的使用多线程并能流畅准确的回答CreateThread与_beg ...

  4. 多线程 CreateThread与_beginthreadex本质区别

    本文参考了 http://blog.csdn.net/morewindows/article/details/7421759 主函数创建一个线程,并且等待它执行完毕 #include <iost ...

  5. CreateThread与_beginthreadex本质区别

    原文地址:http://blog.csdn.net/morewindows/article/details/7421759 使用多线程其实是非常容易的,下面这个程序的主线程会创建了一个子线程并等待其运 ...

  6. CreateThread 和_beginthreadex区别

    本文将带领你与多线程作第一次亲密接触,并深入分析CreateThread与_beginthreadex的本质区别,相信阅读本文后你能轻松的使用多线程并能流畅准确的回答CreateThread与_beg ...

  7. windows线程之CreateThread与_beginthreadex区别详解

    CreateThread函数 CreateThread函数是用于创建线程的Windows函数. CreateThread函数参数介绍 HANDLE CreateThread(LPSECURITY_AT ...

  8. CreateThread、_beginthreadex和AfxBeginThread 的区别

    CreateThread._beginthreadex和AfxBeginThread 创建线程好几个函数可以使用,可是它们有什么区别,适用于什么情况呢? 参考了一些资料,写得都挺好的,这里做一些摘抄和 ...

  9. CreateThread、_beginthreadex、AfxBeginThread

    1.CreateThread._beginthreadex.AfxBeginThread的区别和正确使用: CreateThread是一个Windows的API函数,_beginthreadex是一个 ...

最新文章

  1. 较深度地递归转义过滤
  2. [实验]自举?只不过是电容和二极管捣的乱
  3. java.lang.IllegalArgumentException at org.springframework.asm.ClassReader.init(Unknown Source)
  4. MONTH_NAMES_GET
  5. 循环嵌套-[扩展]print函数的结尾处理
  6. Delphi调用外部程序的集中方法
  7. python安装不上怎么办_python安装不了怎么办
  8. java分页 jar_零基础学java之javaEE,分页
  9. mysql 数据库编码怎么看,查看数据库编码_查看mysql编码方式 mysql数据库编码查看方法...
  10. linux-Centos 7下bond与vlan技术的结合[推荐]
  11. Python3.x:pytesseract识别率提高(样本训练)
  12. 【R语言】如何直接调取Wind、iFinD数据接口教程
  13. 用C#制作PDF文件全攻略
  14. python自动填表单_用python-webdriver实现自动填表
  15. 微软官方的SQLHelper类(含完整中文注释)
  16. vue.js 密码加密_1Password ——密码管理工具
  17. IE8 “Automation 服务器不能创建对象”问题解决方法
  18. 「敏捷」Stacey Matrix模型帮你确定合适的项目管理方法
  19. Time Series FeatuRe Extraction on basis of Scalable Hypothesis tests (tsfresh –A Python package)
  20. 如何用WGDI进行共线性分析(一点五)

热门文章

  1. 常用宏定义 - 系统相关
  2. young people can also be a leader
  3. 读书笔记《集体智慧编程》Chapter 5 : Optimization
  4. 什么MySQL语句在存储过程体中是合法的
  5. 多处理机的进程调度方式
  6. Systemd 入门教程之命令篇
  7. Android中创建自己的对话框
  8. ubuntu进入桌面自动启动脚本_在 Ubuntu 下开机自启动自己的 QT 程序而不启动 Ubuntu 的桌面...
  9. opencv python教程简书_OpenCV-Python系列二:常用的图像属性
  10. Ubuntu 键盘错位解决 更改键盘布局