Windows C 多线程编程的库支持
线程实质上是程序中的执行路径。也是 Win32 安排的最小执行单元。线程包括堆栈、CPU 寄存器的状态和系统计划程序执行列表中的项。每个线程共享所有进程的资源。
进程包括一个或多个线程和代码、数据和内存中的其他程序资源。典型的程序资源是打开的文件、信号灯和动态分配的内存。当系统计划程序给予其中的一个线程执行控制时,即执行程序。计划程序确定应当运行哪些线程以及它们应当何时运行。较低优先级的线程可能必须等到较高优先级的线程完成任务后才能运行。在多处理器计算机上,计划程序可以将单个线程移到不同的处理器以“平衡”CPU 负荷。
进程中的每个线程都独立运行。除非使这些线程相互可见,否则线程分别执行,对进程中的其他线程一无所知。线程共享公共资源,但是,必须使用信号灯或其他进程间的通信方法协调它们的工作。
1、多线程编程的库支持
如果在执行 printf 函数时有线程被 Win32 计划程序挂起,则程序的其他线程中的某一个可能会开始执行。如果第二个线程也调用 printf,数据可能会损坏。为避免这种情况,必须限制对函数使用的静态数据的访问,一次只允许一个线程访问数据。
因为每个线程具有不同的堆栈,所以不需要序列化对基于堆栈的(自动)变量的访问。因此,只使用自动(堆栈)变量的函数是可重入的。标准 C 运行时库(如 LIBC)的可重入函数的数量有限。对于需要使用通常不可重入的 C 运行时库函数的多线程程序,应该使用多线程库 LIBCMT.LIB 来生成。
多线程 C 库:LIBCMT.LIB 和 MSVCRT.LIB
支持库 LIBCMT.LIB 是可重入库,用于创建多线程程序。调用共享的 MSVCRT70.DLL 中代码的 MSVCRT.LIB 库也是可重入库。当应用程序调用这些库中的函数时,可以应用下列规则:
所有库调用必须使用 C (__cdecl) 调用约定;使用其他调用约定(如 __fastcall 或 __stdcall)编译的程序必须使用它们调用的运行时库函数的标准包含文件。
传递到库函数的变量必须由值传递或转换为指针。
使用 LIBCMT.LIB 生成的程序不与它们所调用的任何动态链接库共享 C 运行时库代码或数据。
LIBCMT.LIB 和 MSVCRT.LIB 以外的其他选择
如果不使用 LIBCMT.LIB 来生成多线程程序,必须执行下列操作:
使用标准 C 库并且只允许可重入函数集进行库调用。
使用 Win32 API 线程管理函数,如 CreateThread。
通过使用 Win32 服务(如信号灯和 EnterCriticalSection 及 LeaveCriticalSection 函数),为不可重入的函数提供自己的同步。
警告 多线程库 LIBCMT.LIB 包括 _beginthread 和 _endthread 函数。_beginthread 函数执行初始化,若没有该函数,许多 C 运行时函数将失败。如果要调用 C 运行时函数,必须使用 C 程序中用 LIBCMT.LIB 生成的 _beginthread,而不是 CreateThread。
多线程库编译选项
若要生成使用 C 运行时库的多线程应用程序,必须通知编译器使用特殊版本的库 (LIBCMT.LIB)。若要选择这些库,请首先打开项目的属性页对话框(“视图”菜单)并单击 C/C++ 文件夹。选择“代码生成”页。从“运行时库”下拉框中选择“多线程”。单击“确定”按钮以返回编辑状态。
命令行中的多线程库编译器选项 (/MT) 是用 LIBCMT.LIB 生成多线程程序的最佳途径。在创建新项目的过程中指定多线程应用程序时,自动设置此选项,此选项将 LIBCMT 库名嵌入对象文件中。
2、多线程编程的包含文件
Microsoft Visual C++ 的包含文件包含使用 LIBCMT.LIB 的多线程应用程序的条件部分。若要使用适当的定义编译应用程序,可以:
使用上一节中所述的多线程库编译器选项进行编译。
在源文件中或在命令行上使用 /D 选项定义符号常数 _MT。
在库中实现 C 运行时库函数时,标准包含文件声明它们。如果使用完全优化 (/Ox) 或 fastcall 调用约定 (/Gr) 选项,编译器假设应使用寄存器调用约定调用所有函数。运行时库函数是使用 C 调用约定编译的,标准包含文件中的声明通知编译器生成对这些函数的正确外部引用。
3、线程控制的 C 运行时库函数
所有 Win32 程序都至少有一个线程。任何线程都可以创建附加线程。线程可以快速完成其工作,然后终止;也可以在程序的生存期内保持活动状态。
LIBCMT 和 MSVCRT C 运行时库提供两个用于创建和终止线程的函数:_beginthread 和 _endthread。
_beginthread 函数创建新线程;如果操作成功,则返回线程标识符。线程完成执行时自动终止,或者可通过调用 _endthread 自己终止。
警告 如果要从使用 LIBCMT.LIB 生成的程序调用 C 运行时例程,则必须使用 _beginthread 函数启动线程。不要使用 Win32 函数 ExitThread 和 CreateThread。如果有一个以上的线程在等待挂起的线程完成它对 C 运行时数据结构的访问时被阻塞,使用 SuspendThread 会导致死锁。
_beginthread 函数
_beginthread 函数创建新线程。线程与进程中的其他线程共享进程的代码和数据段,但是线程具有自己的唯一寄存器值、堆栈空间和当前指令地址。系统给予每个线程 CPU 时间,使进程中的所有线程都可以同时执行。
_beginthread 函数与 Win32 API 中的 CreateThread 函数类似,但有如下差异:
_beginthread 函数使您可以