多线程编程指南

编译多线程应用程序

许多选项可用于头文件、定义标志和链接。

为编译做准备

编译和链接多线程程序时,需要以下项。Solaris 软件应包括除 C 编译器以外的所有项。

选择 Solaris 语义或 POSIX 语义

某些函数在 POSIX 标准中的语义与在 Solaris 2.4 发行版中的语义是不同的,Solaris 2.4 发行版基于早期的 POSIX 草案。函数定义是在编译时选择的。有关预期参数和返回值中差异的说明,请参见手册页 section 3: Library Interfaces and Headers。以下是具有不同语义的函数:

在 Solaris 9 和以前的发行版中,Solaris fork(2) 函数可以复制所有的线程 fork-all 行为。POSIX fork(2) 函数仅复制调用线程 fork-one 行为,与 Solaris fork1() 函数是一样的。

从 Solaris 10 发行版开始,fork() 的行为在未链接到 -lpthreaad 时可能会发生更改,以与 POSIX 版本保持一致。需要特别指出的是,fork() 被重新定义为fork1()。因此,fork() 将复制子进程中的调用线程。所有 Solaris 发行版中都支持 fork1() 的行为。新函数 forkall() 可以针对需要将父进程的所有线程复制到子进程中的应用程序提供此行为。

包括 <thread.h><pthread.h>

包括文件 <thread.h> 可以编译与早期的 Solaris 软件发行版向上兼容的代码。此文件包含 Solaris 线程接口的声明。要使用 POSIX 线程调用 thr_setconcurrency(3C),程序需要包括 <thread.h>

包括文件 <pthread.h>(与 -lpthread 库结合使用)可以编译符合 POSIX 标准定义的多线程接口的代码。为了与 POSIX 完全符合,应该将功能测试宏 _POSIX_C_SOURCE 的值 (long) 设置为 ≥ 199506。请参见 standards(5) 手册页。

对于 1996 版 POSIX 标准:

cc89 -D_POSIX_C_SOURCE=199506L [flags] file

对于 2001 版 POSIX 标准:

cc99 -D_POSIX_C_SOURCE=200112L [flags] file ... [-l rt]

可以在同一个应用程序中混合使用 Solaris 线程与 POSIX 线程。请在应用程序中同时包括 <thread.h><pthread.h>

如果二者混合使用,则当使用 -D_REENTRANT 编译时,将采用 Solaris 语义,而当使用 -D_POSIX_C_SOURCE 编译时,将采用 POSIX 语义。

定义 _REENTRANT_POSIX_C_SOURCE

对于 POSIX 行为,请使用 -D_POSIX_C_SOURCE 标志集 ≥ 199506L 来编译应用程序。对于 Solaris 行为,请使用 -D_REENTRANT 标志来编译多线程程序。这些编译器标志适用于应用程序的每个模块。

对于混合的应用程序,具有 POSIX 语义的 Solaris 线程使用 -D_REENTRANT-D_POSIX_PTHREAD_SEMANTICS 标志进行编译。

要编译单线程应用程序,请不要定义 -D_REENTRANT 标志,也不要定义 -D_POSIX_C_SOURCE 标志。不存在这些标志时,errnostdio 等的所有原有定义仍然生效。


注 –

请在不使用 -D_REENTRANT 标志的条件下编译单线程应用程序。使用这种方式编译单线程应用程序,以避免将宏(如 putc(3s))转换为可重复执行函数调用时引起的性能降低。


总之,定义 -D_POSIX_C_SOURCE 的 POSIX 应用程序将获取例程的 POSIX 语义。仅定义 -D_REENTRANT 的应用程序将获取这些例程的 Solaris 语义。定义 -D_POSIX_PTHREAD_SEMANTICS 的 Solaris 应用程序将获取这些例程的 POSIX 语义,但仍然可以使用 Solaris 线程接口。

同时定义 -D_POSIX_C_SOURCE-D_REENTRANT 的应用程序将获取 POSIX 语义。

使用 libthreadlibpthread 链接

对于 POSIX 线程行为(在 Solaris 9 和以前的发行版中),请装入 libpthread 库。对于 Solaris 线程行为,请装入 libthread 库。有的 POSIX 程序员可能想使用 -lthread 进行链接,以保留 fork()fork1() 之间的 Solaris 区别。-lpthread 库使 fork() 的行为方式与 Solaris fork1() 调用的行为方式相同。

在 Solaris 10 和后续发行版中,两个线程库都不再是必需的,但是仍然可以为了实现兼容而指定库。所有的线程功能都已被移入标准 C 库中。要使用 libthread,请在 ld 命令行的 lc 前面指定 -lthread,或在 cc 命令行的末尾指定 -lthread

要使用 libthread,请在 ld 命令行的 -lc 前面指定 -lthread,或在 cc 命令行的末尾指定 -lthread

要使用 libpthread,请在 ld 命令行的 -lc 前面指定 -lpthread,或在 cc 命令行的末尾指定 -lpthread

在 Solaris 9 发行版之前,不应使用 -lthread-lpthread 来链接非线程程序。这样做将在链接时建立在运行时启动的多线程机制。这些机制将使单线程应用程序的速度变慢,浪费系统资源,而且会在调试代码时产生误导性结果。

在 Solaris 9 和后续发行版中,使用 -lthread-lpthread 链接非线程应用程序时不会为程序产生语义差异。也不会创建额外的线程或额外的 LWP。只有主线程会像传统的单线程进程一样执行操作。对程序的唯一影响就是使系统库锁定成为实际锁定,与伪函数调用相反。您必须为获取无竞争锁定付出代价。

在 Solaris 10 发行版之前,如果应用程序没有链接 -lthread-lpthread,则对 libthreadlibpthread 的所有调用都为空操作指令。运行时库 libc 具有许多预定义 libthreadlibpthread 存根,这些存根都是空过程。当应用程序同时链接了 libc 和线程库时,将通过 libthreadlibpthread 插入实际过程。


注 –

对于使用线程的 C++ 程序,请使用 -mt 选项(而不是 -lthread)来编译和链接应用程序。-mt 选项与 libthread 链接,并且能确保正确的库链接顺序。-lthread 可能会导致程序进行核心转储。


在 POSIX 环境中链接

对于 1996 版 POSIX 标准,请使用以下选项来编译和链接应用程序:

cc89 -D_POSIX_C_SOURCE=199506L [flags] file ... [-l rt]

对于 2001 版 POSIX 标准,请使用以下选项来编译和链接应用程序:

cc99 -D_POSIX_C_SOURCE=200112L [flags] file ... [-l rt]

在 Solaris 环境中链接

在 Solaris 线程环境中,请使用以下选项来编译和链接应用程序:

cc -D_REENTRANT -D POSIX_THREAD_SEMANTICS [flags] file ... [-l rt]

在混合环境中链接

在混合环境中,请使用以下选项来编译和链接应用程序:

cc -D_REENTRANT [flags] file ... [-l rt]

在混合使用时,需要包括 thread.hpthread.h

与 POSIX 信号的 -lrt 链接

Solaris 信号例程 sema_*(3C) 包含在标准的 C 库中。相对而言,您可以链接 -lrt 库,从而获取使用信号进行同步中所述的标准 sem_*(3R) POSIX 信号例程。

将原有模块与新模块链接

表 7–1 说明,在将多线程对象模块与原有对象模块链接时应格外小心。

表 7–1 使用或不使用 _REENTRANT 标志进行编译

文件类型 

编译 

参考 

返回值 

原有对象文件(非线程)和新对象文件 

不使用 _REENTRANT_POSIX_C_SOURCE 标志

静态存储

传统的 errno

新对象文件 

使用 _REENTRANT_POSIX_C_SOURCE 标志

__errno,新的二进制入口点

线程的 errno 定义地址

libnsl 中使用 TLI 的程序要获取 TLI 全局错误变量,请包括 tiuser.h

使用 _REENTRANT_POSIX_C_SOURCE 标志(必需)

__t_errno,新的入口点

线程的 t_errno 定义地址。