`
mywebcode
  • 浏览: 988283 次
文章分类
社区版块
存档分类
最新评论

Chapter06-C/C++运行库

 
阅读更多

在上一篇关于线程的讲解中,有提到一般我们都不应该直接调用CreateThread函数去创建新线程,而是调用_beginthreadex函数创建新线程。

以下是_beginthreadex函数的伪代码:

uintptr_t __cdecl _beginthreadex (
	void *psa,
	unsigned cbStackSize,
	unsigned (__stdcall * pfnStartAddr) (void *),
	void * pvParam,
	unsigned dwCreateFlags,
	unsigned *pdwThreadID) {
		_ptiddata ptd; // Pointer to thread's data block
		uintptr_t thdl; // Thread's handle
		// Allocate data block for the new thread.
		if ((ptd = (_ptiddata)_calloc_crt(1, sizeof(struct _tiddata))) == NULL)
			goto error_return;
		// Initialize the data block.
		initptd(ptd);
		// Save the desired thread function and the parameter
		// we want it to get in the data block.
		ptd->_initaddr = (void *) pfnStartAddr;
		ptd->_initarg = pvParam;
		ptd->_thandle = (uintptr_t)(-1);
		// Create the new thread.
		thdl = (uintptr_t) CreateThread((LPSECURITY_ATTRIBUTES)psa, cbStackSize,
			_threadstartex, (PVOID) ptd, dwCreateFlags, pdwThreadID);
		if (thdl == 0) {
			// Thread couldn't be created, cleanup and return failure.
			goto error_return;
		}
		// Thread created OK, return the handle as unsigned long.
		return(thdl);
error_return:
		// Error: data block or thread couldn't be created.
		// GetLastError() is mapped into errno corresponding values
		// if something wrong happened in CreateThread.
		_free_crt(ptd);
		return((uintptr_t)0L);
}


关于_beginthreadex函数我们需要注意:

  • 每个线程从C/C++运行库堆中获取自己的_tiddata内存块。
  • 传递给_beginthreadex的线程函数地址记录在_tiddata内存块中。
  • _beginthreadex函数内部调用了CreateThread函数,因为这是操作系统知道的创建新线程的唯一方法。
  • 当CreateThread函数被调用时,它会被告知要开始执行_threadstartex函数(而不是pfnStartAddr)去开始一个新线程。同时也需要注意,此时传递过去的参数是_tiddata结构体地址而不是pvParam.
  • 如果一切顺利的话,就会像CreateThread函数一样返回线程句柄。如果操作失败,则返回0值。


////////////////////////////////////////////////////////////////


上面提到了重要的_threadstartex函数,下面是它的伪代码:

static unsigned long WINAPI _threadstartex (void* ptd) {
	// Note: ptd is the address of this thread's tiddata block.
	// Associate the tiddata block with this thread so
	// _getptd() will be able to find it in _callthreadstartex.
	TlsSetValue(__tlsindex, ptd);
	// Save this thread ID in the _tiddata block.
	//Windows via C/C++, Fifth Edition by Jeffrey Richter and Christophe Nasarre
		((_ptiddata) ptd)->_tid = GetCurrentThreadId();
	// Initialize floating-point support (code not shown).
	// call helper function.
	_callthreadstartex();
	// We never get here; the thread dies in _callthreadstartex.
	return(0L);
}
static void _callthreadstartex(void) {
	_ptiddata ptd; /* pointer to thread's _tiddata struct */
	// get the pointer to thread data from TLS
	ptd = _getptd();
	// Wrap desired thread function in SEH frame to
	// handle run-time errors and signal support.
	__try {
		// Call desired thread function, passing it the desired parameter.
		// Pass thread's exit code value to _endthreadex.
		_endthreadex(
			((unsigned (WINAPI *)(void *))(((_ptiddata)ptd)->_initaddr))
			(((_ptiddata)ptd)->_initarg)) ;
	}
	__except(_XcptFilter(GetExceptionCode(), GetExceptionInformation())){
		// The C run-time's exception handler deals with run-time errors
		// and signal support; we should never get it here.
		_exit(GetExceptionCode());
	}
}

关于_threadstartex函数,需要注意:

  • 一个新线程最开始执行RtlUserThreadStart函数,然后跳到_threadstartex。
  • 新线程的_tiddata数据块是_threadstartex函数唯一的参数。
  • TlsSetValue函数是一个将一个数值关联到线程的系统函数。
  • 在无参数的_callthreadstartex函数中,有一个SEH帧,它将预期要执行的线程函数包围起来。这个SEH帧处理许多鱼运行库相关的事情(比如运行错误等)。
  • 接下来就执行预期函数pfnStartAddr,并传递预期参数pvParam。pfnStartAddr和pvParam之前被记录在_tiddata块中。
  • 预期的线程函数返回值就是线程的退出码。值得注意的是:_callthreadstartex不仅仅是返回至_threadstartex然后再返回到RtlUserThreadStart;如果真的这样做,线程会消亡,退出码也能正确设置,但是线程的_tiddata内存块不会被销毁。这样将会导致你的程序出现内存泄漏。为了防止这种情况,就需要调用C/C++运行库函数--_endthreadex。



////////////////////////////////////////////////////////////////


下面就来介绍_endthreadex函数,下面就是其伪代码:

void __cdecl _endthreadex (unsigned retcode) {
	_ptiddata ptd; // Pointer to thread's data block
	// Clean up floating-point support (code not shown).
	// Get the address of this thread's tiddata block.
	ptd = _getptd_noexit ();
	// Free the tiddata block.
	if (ptd != NULL)
		_freeptd(ptd);
	// Terminate the thread.
	ExitThread(retcode);
}

对于_endthreadex函数,我们需要注意的是:

  • 当你的线程函数返回时,_beginthreadex函数会调用_endthreadex函数。
  • C运行库的_getptd_noexit函数内部调用操作系统的TlsGetValue函数,TlsGetValue函数获取调用线程的tiddata内存块地址。
  • 这个_tiddata数据块之后被释放,然后调用操作系统ExitThread函数去真正销毁线程。

在实际编程过程中,我们也不应该直接调用ExitThread函数去退出一个线程,而是调用_endthreadex函数。原因有两个:

  • 调用ExitThread会杀死线程,而不会让执行的线程函数返回。如果线程函数没有返回,则函数内的C++对象就不会被销毁。
  • 调用ExitThread退出程序后,不会回收_tiddata内存块。这样你的应用程序存在内存泄漏。

分享到:
评论

相关推荐

    交叉编译器

    http://man.chinaunix.net/linux/lfs/htmlbook/chapter06/chapter06.html参考具体的gcc相关软件安装 本人的联系方式为:549827768@qq.com,西华大学 我们采用crosstool0.42来作为我们编译交叉编译工具链的脚本。详细...

    基于Linux的C++ 教程合集, 包括C++基础, C++服务器, C++专题.rar

    所有必需的开发环境搭建以及工具选择安装好之后,这里通过一个完整的实际程序例子来演示一下Linux系统下C++程序开发的整个过程,让初学者对于Linux下C++应用开发有一个初步的印象。 打开UE编辑器,单击软件界面上...

    DirectShow

    /---------------------------------------------------------------------\ * 书 名:《DirectShow开发指南》 * 作 者: 陆其明(著) ...\---------------------------------------------------------------------/

    MySQL 5.1官方简体中文参考手册

    1.8.3. 在ANSI模式下运行MySQL 1.8.4. MySQL对标准SQL的扩展 1.8.5. MySQL与标准SQL的差别 1.8.6. MySQL处理约束的方式 2. 安装MySQL 2.1. 一般安装问题 2.1.1. MySQL支持的操作系统 2.1.2. 选择要安装的MySQL分发版...

    vc++ 开发实例源码包

    chapter7 实现了声音录制等功能。 CHtmlViewProjV2 详细演示了HtmlView的使用与HtmlView事件站点拦截的实现、js调用。 CIVStringSet_Demo 自定义了一个类似STL容器的类,并进行了测试。 ClearHistory 实现了 清楚...

    Windows环境下32位汇编语言程序设计_随书光盘

    Chapter06\Timer ;定时器的使用 Chapter07\DcCopy ;在两个窗口的 DC 间互相拷贝屏幕 Chapter07\Clock ;模拟时钟程序 Chapter07\BmpClock ;用 Bitmap 图片做背景的模拟时钟程序 Chapter07\TestObject ;一些常见的...

    Hands-On-Game-Animation-Programming:Packt出版的动手游戏动画编程

    动手C ++游戏动画编程 这是Packt发布的“ 的代码库。 从理论到使用C ++和OpenGL实施学习现代动画技术这本书是关于什么的? 动画是任何游戏中最重要的部分之一。 现代动画系统直接与轨道驱动的动画配合使用,并为诸如...

    ICE分布式程序设计中文版

    12.1 Chapter Overview 279 12.2 引言 279 12.3 服务器端 main函数 280 12.4 接口的映射 285 12.5 参数传递 287 12.6 引发异常 288 12.7 Tie 类 289 12.8 对象体现 292 12.9 总结 296 第 13 章开发 Java 文件系统...

    vc++ 应用源码包_6

    chapter7 实现了声音录制等功能。 CHtmlViewProjV2 详细演示了HtmlView的使用与HtmlView事件站点拦截的实现、js调用。 CIVStringSet_Demo 自定义了一个类似STL容器的类,并进行了测试。 ClearHistory 实现了 清楚...

    CPP-Data-Structures-and-Algorithms:Packt出版的《 C ++数据结构和算法》

    本书将是您的伴侣,因为它将带您逐步实现经典的数据结构和算法,以帮助您以有信心的C ++程序员的身份起步并运行。 说明和导航 所有代码都组织在文件夹中。 每个文件夹均以数字开头,后跟应用程序名称。 例如,...

    积分java源码-TheCSharpPlayer-sGuide-2ndEd:使用RBWhitaker的«TheC#Player'sGuide-

    威力(C++ 更强大) .NET 框架 语言平台 两部分 公共语言运行时 (CLR):一个虚拟机,它实际执行代码 [human]->source code->[C# compiler]->Common Intermediate Language CIL or IL in .exe or .dll->[CLR compiler...

    PT80-NEAT开发指南v1.1

    编译及运行程序(模拟器下) ................................................................................................................ 7 编译及运行程序(PT80) ......................................

    LearningCPPFunctionalProgramming:Packt出版的《学习C ++函数式编程》

    这是发布的“ 的代码库。 它包含从头到尾完成本书所必需的所有支持项目文件。 关于这本书 通过功能编程,开发人员可以将程序划分为较小的可重用组件,从而简化整个软件的创建,测试和维护。 结合C ++的功能,您可以...

    vc++ 应用源码包_1

    chapter7 实现了声音录制等功能。 CHtmlViewProjV2 详细演示了HtmlView的使用与HtmlView事件站点拦截的实现、js调用。 CIVStringSet_Demo 自定义了一个类似STL容器的类,并进行了测试。 ClearHistory 实现了 清楚...

    vc++ 应用源码包_2

    chapter7 实现了声音录制等功能。 CHtmlViewProjV2 详细演示了HtmlView的使用与HtmlView事件站点拦截的实现、js调用。 CIVStringSet_Demo 自定义了一个类似STL容器的类,并进行了测试。 ClearHistory 实现了 清楚...

    vc++ 应用源码包_5

    chapter7 实现了声音录制等功能。 CHtmlViewProjV2 详细演示了HtmlView的使用与HtmlView事件站点拦截的实现、js调用。 CIVStringSet_Demo 自定义了一个类似STL容器的类,并进行了测试。 ClearHistory 实现了 清楚...

    vc++ 应用源码包_3

    chapter7 实现了声音录制等功能。 CHtmlViewProjV2 详细演示了HtmlView的使用与HtmlView事件站点拦截的实现、js调用。 CIVStringSet_Demo 自定义了一个类似STL容器的类,并进行了测试。 ClearHistory 实现了 清楚...

    Mastering-OpenCV3-Second-Edition:精通OpenCV 3-Packt第二版

    这是发行的的代码存储库。 它包含从头到尾完成本书所必需的所有支持项目文件。 关于这本书 本书将使您直接从事创建功能强大且独特的计算机视觉应用程序的工作。 每章都围绕一个中心项目展开,深入探讨OpenCV重要方面...

    Learning-Vulkan:Packt发布的学习Vulkan的代码存储库

    这是发布的的代码存储库。 它包含从头到尾完成本书所必需的所有支持项目文件。 关于这本书 下一代图形和计算API Vulkan是Khronos的最新产品。 该API是OpenGL的后继产品,相比之下,它提供了极大的灵活性和高性能,...

Global site tag (gtag.js) - Google Analytics