CreateThread和_beginthreadex的区别

网址:

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类型变量一样。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/462460.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

配置Xmanager 连接AIX服务器

步骤1. 确认系统是否有安装X11.Dt的相关程序包,该程序包在系统盘中或者直接安装X11.Dt文件集X11.Dt.ToolTalkX11.Dt.bitmaps X11.Dt.helpmin X11.Dt.helprun X11.Dt.lib X11.Dt.ToolTalkX11.Dt.bitmaps X11.Dt.helpmin 步骤2、安装完后,需要做如下动作启…

分布式版本控制系统入门

简介 在过去几年,对于分布式版本控制可以给开发过程提供的益处有许多争论。最近,分布式工具已经很成熟了。尽管分布式工具的一些优点最初可能不明显,但是从长期来看,它们提供的灵活性是非常有意义的。阅读完本文之后,您…

Hadoop源生实用工具之distcp

1 概览 DistCp(Distributed Copy)是用于大规模集群内部或者集群之间的高性能拷贝工具。 它使用Map/Reduce实现文件分发,错误处理和恢复,以及报告生成。 它把文件和目录的列表作为map任务的输入,每个任务会完成源列表中…

如何使用spy ++ (How to use Spy ++)

一个网友在我的一篇随笔后问道,如何使用spy 查找要用到的窗体类名以及相关信息 现把相关作法整理如下 如果你装了vs.net,在vs.net的工具里面就可以看到spy 打开spy ,会以树状图显示当前系统所有的窗体信息,如下所示 然后找到…

[Java]关于throw,throws,try{}catch(){}

一、异常的产生1. 在java代码中,如果发生异常,JVM就会中断程序代码的运行,然后创建异常对象并抛出。例如:int a2, b0;int ia/b; //除数不能为0,发生异常System.out.println(i);程序会在执行第2行代码时中断&#xff0…

PL/SQL Developer 9 注册机

软件下载:下载 注册机下载:下载 说明:测试版本--9.0.1.1613(无需替换文件,输入注册信息即可) 附图: 转载于:https://www.cnblogs.com/zhanqi/archive/2011/10/12/2208750.html

程序图片运行效果存在偏差问题

2019独角兽企业重金招聘Python工程师标准>>> 当图片放到高分辨率的文件夹下,运行程序的手机却是低分辨率时就会出现该问题。解决办法很容易,就是把图片换个文件夹。比如图片放到drawable-xhdpi下,而模拟器是480*800,图…

如何将android例子程序添加到Eclipse进行调试运行

website:http://blog.csdn.net/whwzm88/article/details/6342583 1、打开Eclipse,选择File->new->Android Project,如附图所示窗口: 2、选择Create project from existing source单选框,然后点击->Browse..., 找到想要查看的android例子目录: 备注&…

计算几何——圆卡精度cf1059D

double 在1e17以后就不能顾及小数&#xff0c;所以用一下加精度的技巧 sqrt(r*r-d*d)sqrt(rd)*sqrt(r-d) 遇到误差在几位以内的注意要修改二分的精度&#xff0c;用最大的数据去乘以精度即可 #include<bits/stdc.h> using namespace std;const double esp 1e-7; const d…

坦克大战 Java版

由于博客开通不久&#xff0c;以前没有博客&#xff0c;顺便把以前做的项目也介绍下 希望大家支持&#xff0c;谢谢 坦克大战 IDE&#xff1a;MyEclipse 9.0 语言&#xff1a;Java SE 数据库&#xff1a;由于是单机版&#xff0c;不需要较高的安全性&#xff0c;且数据量较小&a…

c# 扩展方法奇思妙用高级篇五:ToString(string format) 扩展

在.Net中&#xff0c;System.Object.ToString()是用得最多的方法之一&#xff0c;ToString()方法在Object类中被定义为virtual&#xff0c;Object类给了它一个默认实现&#xff1a; 1 publicvirtualstringToString()2 {3 returnthis.GetType().ToString();4 }.Net中原生的class…

MFC中OnDraw与OnPaint的区别

OnPaint是WM_PAINT消息的消息处理函数&#xff0c;在OnPaint中调用OnDraw&#xff0c;一般来说&#xff0c;用户自己的绘图代码应放在OnDraw中。 OnPaint()是CWnd的类成员&#xff0c;负责响应WM_PAINT消息。OnDraw()是CVIEW的成员函数&#xff0c;没有响应消息的功能.当视图变…

Windows Internet

网址:http://baike.baidu.com/view/560670.htm WinInet 求助编辑百科名片 WinInet&#xff08;“Windows Internet”&#xff09;API帮助程序员使用三个常见的Internet协议&#xff0c;这三个协议是用于World Wide Web万维网的超文本传输协议&#xff08;HTTP&#xff1a;Hyper…

PHP使用CURL抓取页面

cURL的基本原理 curl是利用URL语法在命令行方式下工作的开源文件传输工具&#xff0c;他能够从互联网上获得各种各样的网络资源。简单来说&#xff0c;curl就是抓取页面的升级版。 <?php//1.初始化&#xff0c;创建一个新cURL资源$ch curl_init(); //2.设置URL和相应的选…

Lync Server 2010迁移至Lync Server 2013故障排错 Part3 :内外网共享PPT提示证书问

最近在公司生产环境中部署Lync Server 2013并对公网发布后&#xff0c;发现公网未加域客户端在与内网用户共享PPT时&#xff0c;会报证书错误&#xff0c;如下图所示&#xff1a;但是内网是正确的&#xff0c;经过查询了一下Lync 客户端自身的日志信息后发现&#xff0c;公网客…

用WinInet开发Internet客户端应用指南

一&#xff1a;http://www.vckbase.com/document/viewdoc/?id545 二&#xff1a;http://www.vckbase.com/document/viewdoc/?id546 用WinInet开发Internet客户端应用指南&#xff08;一&#xff09; 编译/NorthTibet 一、概述一个Internet客户端程序的目的是通过Internet…

WebService的基本概念:java webservice,什么是webservice

WebService是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求&#xff0c;轻量级的独立的通讯技术。 这种技术允许网络上的所有系统进行交互。随着技术的发展&#xff0c;一个Web服务可以包含额外的指定功能并且可以在多个B2B应用中协作通讯。 Web服务可以理解…

掌握常见的内部排序方法(插入排序,冒泡排序,选择排序,快速排序,堆排序,希尔排序,归并排序,基数排序等)...

掌握常见的内部排序方法&#xff08;插入排序&#xff0c;冒泡排序&#xff0c;选择排序&#xff0c;快速排序&#xff0c;堆排序&#xff0c;希尔排序&#xff0c;归并排序&#xff0c;基数排序等&#xff09;。数组高级以及Arrays(掌握)排序方法空间复杂度时间复杂度稳定性插…

关于Android错误 View requires API level 14 (current...

2019独角兽企业重金招聘Python工程师标准>>> 问题描述&#xff1a;在界面配置文件main.xml中&#xff0c;可能用上某些控件&#xff08;例如GridLayout&#xff09;遇上下面所说的错误:View requires API level 14(current min is 8), 但将项目clear后&#xff0c;再…

socket-select函数

最近在用socket编程&#xff0c;用到select函数&#xff0c;于是上网查找资料&#xff0c;如下这篇是讲的比较详细易懂的&#xff1a;Select在Socket编程中还是比较重要的&#xff0c;可是对于初学Socket的人来说都不太爱用Select写程序&#xff0c;他们只是习惯写诸如 connect…