windows笔记-创建线程的另一个函数_beginthreadex
来源:互联网 发布:cae软件下载 编辑:程序博客网 时间:2024/04/29 03:45
在多线程环境中存在问题的C/C++运行期库变量和函数包括errno、_doserrno、strtok、_wcstok、strerror、_strerror、tmpnam、tmpfile、asctime、_wasctime、gmtime、_ecvt和_fcvt等。
所以如果使用上面的变量或函数的话,若要创建一个新线程,绝对不要调用操作系统的CreateThread函数,必须调C/C++运行期库函数_beginthreadex:
uintptr_t_beginthreadex(
void*security,
unsignedstack_size,
unsigned( *start_address )( void * ),
void*arglist,
unsignedinitflag,
unsigned*thrdaddr
);
注意,_beginthreadex函数只存在于C/C + +运行期库的多线程版本中。
_beginthreadex的一些要点:
* 每个线程均获得由C/C++运行期库的堆栈分配的自己的tiddata内存结构。(tiddata结构位于Mtdll.h文件中的Visual C++源代码中)。
* 传递给_beginthreadex的线程函数的地址保存在tiddata内存块中。传递给该函数的参数也保存在该数据块中。
* _beginthreadex确实从内部调用CreateThread,因为这是操作系统了解如何创建新线程的唯一方法。
* 当调用CreateThread时,它被告知通过调用_threadstartex而不是pfnStartAddr来启动执行新线程。传递给线程函数的参数是tiddata结构而不pvParam的地址。
* 如果一切顺利,就会像CreateThread那样返回线程句柄。如果任何操作失败了,便返回
NULL。
_ threadstartex的一些重点:
* 新线程开始从BasethreadStart函数(在kernel32.dll文件中)执行,然后转移到_threads tartex。
* 到达该新线程的tiddata块的地址作为其唯一参数被传递给_threadstartex。
* TlsSetValue是个操作系统函数,负责将一个值与调用线程联系起来。_threadstartex函数tiddata块与线程联系起来。
* 一个SEH帧被放置在需要的线程函数周围。这个帧负责处理与运行期库相关的许多事情—例如,运行期错误(比如放过了没有抓住的C++异常条件)和 C/C++运行期库的signal函数。这是特别重要的。如果用CreateThread函数来创建线程,然后调用C/ C + +运行期库的signal函数,那么该函数就不能正确地运行。
* 调用必要的线程函数,传递必要的参数。记住,函数和参数的地址由_beginthreadex保存在tiddata块中。
* 必要的线程函数返回值被认为是线程的退出代码。注意,_threadstartex并不只是返回到BaseThreadStart。如果它准备这样做,那么线程就终止运行,它的退出代码将被正确地设置,但是线程的tiddata内存块不会被撤消。这将导致应用程序中出现一个漏洞。若要防止这个漏洞,可以调用另一个C/C++运行期库函数_endthreadex ,并传递退出代码。
_endthreadex的一些要点:
* C运行期库的_getptd函数内部调用操作系统的TlsGetValue函数,该函数负责检索调用线程的tiddata内存块的地址。
* 然后该数据块被释放,而操作系统的ExitThread函数被调用,以便真正撤消该线程。当然,退出代码要正确地设置和传递。
ExitThread 函数将撤消调用函数,并且不允许它从当前执行的函数返回。由于该函数不能将任何 C ++对象撤消。避免调用ExitThread的另一个原因是,它会使得线程的tiddata内存块无法释放,如果真的想要强制撤消线程,可以让它调用_endthreadex(而不是调用ExitThread)以便释放线程的tiddata块,然后退出。不过建议不要调用_endthreadex函数。
一旦数据块被初始化并且与线程联系起来,线程调用的任何需要单线程实例数据的C/C ++运行期库函数都能很容易地(通过TlsGetValue)检索调用线程的数据块地址,并对线程的数据进行操作。
当一个线程调用要求tiddata结构的C/C++运行期库函数时,将会发生下面的一些情况(大多数 C/C++运行期库函数都是线程安全函数,不需要该结构)。首先,C/C++运行期库函数试图 (通过调用TlsGetValue)获取线程的数据块的地址。如果返回NULL作为tiddata块的地址,调用线程就不拥有与该地址相关的tiddata块。这时,C/C ++运行期库函数就在现场为调用线程分配一个tiddata块,并对它进行初始化。然后该tiddata块(通过TlsSetValue)与线程相关联。此时,只要线程在运行,该tiddata将与线程待在一起。这时,C/C++运行期库函数就可以使用线程的tiddata块,而且将来被调用的所有C/C + +运行期函数也能使用tiddata块。
这看来有些奇怪,因为线程运行时几乎没有任何障碍。实际上还是存在一些问题。
首先,如果线程使用C/C++运行期库的signal函数,那么整个进程就会终止运行,因为结构化异常处理帧尚未准备好(见上面:_ threadstartex的一些重点)。
第二,如果不是调用 _endthreadex来终止线程的运行,那么数据块就不会被撤消,内存泄漏就会出现(因为没有人会调用CreateThread函数创建的线程,而调用_endthreadex终止线程)。
如果程序模块链接到多线程DLL版本的C/C++运行期库,那么当线程终止运行并释放tiddat块(如果已经分配了tiddata块的话)时,该运行期库会收到一个DLL_THREAD_DETACH通知。尽管这可以防止tiddata块的泄漏,但是强烈建议使用_bdginthreadex而不是使用Createthread来创建线程。
本文地址:http://www.cnblogs.com/fangyukuan/archive/2010/09/02/1816104.html
- windows笔记-创建线程的另一个函数_beginthreadex
- windows笔记-创建线程的另一个函数_beginthreadex
- -创建线程的另一个函数_beginthreadex
- _beginthreadex创建线程函数的使用
- 封装windows 线程创建函数_beginthreadex和createthread函数的类
- windows核心编程笔记第10篇 (线程创建的过程和基于C/C++运行库的_beginthreadex)
- 用_beginthreadex创建线程
- 使用_beginthreadex()创建线程
- _beginthreadex创建线程
- 用_beginthreadex创建线程
- 创建线程函数区别CreateThraed/_beginthreadex/AfxBeginThread(重点理解)
- 创建线程函数区别CreateThraed/_beginthreadex/AfxBeginThread(重点理解)
- 为何创建线程推荐_beginthreadex()函数而非CreateThread()?
- C语言——_beginthreadex函数创建子线程
- 创建线程的方法【CreateThread, AfxBeginThread,_beginthread, _beginthreadex的区别】
- Code::blocks 下用_beginthreadex创建一个简单的线程
- windows线程创建函数
- windows线程同步[CreateThread与_beginthreadex的不同之处]
- 测试
- 算法导论 4.6
- javaScript checkbox全选
- PHP利用Curl、socket、file_get_contents POST数据
- 关于const的全面总结
- windows笔记-创建线程的另一个函数_beginthreadex
- SWT窗口居中的设置
- 安装完oracle后,发现计算机名不符合要求
- poj 2400 Supervisor,Supervisee
- hello android v3 (1):r.java中的layout
- 'operator <<' is ambiguous
- int 和 String 互相转换的多种方法
- mysql数据据存储引擎InnoDB和MyISAM的优势及区别
- struts2中格式化输出数字和日期的方法