开发多线程程序

开发多线程程序与开发带有多个进程的程序类似。 程序的开发也由编译和调试代码组成。

编译多线程程序

本节说明如何生成多线程程序。 它描述了以下内容:
  • 必需的头文件
  • 调用用来生成多线程程序的编译器。

头文件

所有使用线程库的原型、宏和其他定义均在 pthread.h 头文件中,该文件在 /usr/include 目录下。 pthread.h 头文件必须包含在每个使用线程库的源文件中。

pthread.h 头包含 unistd.h 头,后者提供以下全局定义:

_POSIX_REENTRANT_FUNCTIONS
指定应该是重入的所有函数。 多个头文件使用此符号定义辅助重入子例程,例如:localtime_r 子例程。
_POSIX_THREADS
表示 POSIX 线程 API。 此符号用来检查 POSIX 线程 API 是否可用。 宏或者子例程可能使用其他方法定义,这取决于是否使用了 POSIX 或者某些其他线程 API。

pthread.h 文件还包含 errno.h,在后者中,errno 全局变量重新定义为特定于线程。 因此,errno 标识在多线程程序中不再是左值

调用编译器

编译多线程程序时,使用下列其中一个命令调用 C 编译器:
xlc_r
ansi 的缺省语言级别调用编译器
cc_r
扩展的缺省语言级别调用编译器

这些命令可确保使用适当的选项和库,以符合单一 UNIX 规范第 2 版。 POSIX 线程规范 1003.1c 是 单一 UNIX 规范 V 2的子集。

使用 xlc_rcc_r 命令时,下面的库被自动链接到您的程序:
libpthreads.a
线程库
libc.a
标准 C 库
例如,下面的命令编译 foo.c 多线程的 C 源文件并产生 foo 可执行文件:
cc_r -o foo foo.c

对 POSIX 1003.1c Draft 7 调用编译器

AIX®为 Draft 7 应用程序提供源代码兼容性。 建议开发者将他们的线程应用程序移植到最新标准。

编译对线程的 Draft 7 支持的多线程程序时,要使用以下命令之一调用 C 编译器:
xlc_r7
ansi 的缺省语言级别调用编译器
cc_r7
扩展的缺省语言级别调用编译器
使用 xlc_r7cc_r7 命令时,下面的库被自动链接到您的程序:
libpthreads_compat.a
Draft 7 兼容性线程库
libpthreads.a
线程库
libc.a
标准 C 库

要实现源代码兼容性,请使用编译器伪指令 _AIX_PTHREADS_D7。 还需要按照下面的顺序链接库:libpthreads_compat.alibpthreads.alibc.a。 大多数用户不需要知道这些信息,因为命令将提供必要的选项。 为那些没有最新 AIX 编译器的用户提供了这些选项。

Porting draft 7 applications to the &Symbol.unixspec;

Draft 7 和最终标准之间存在的差别包括:
  • 较小的 errno 差别。 最普遍的是使用 ESRCH 表示找不到指定的 pthread。 Draft 7 对此故障通常返回 EINVAL
  • 创建 pthread 时,缺省状态是可连接。 这是很大的更改,因为如果忽略会导致内存泄漏。
  • 缺省 pthread 调度参数是 scope
  • pthread_yield 子例程被 sched_yield 子例程替换。
  • 与互斥锁关联的各种不同的调度策略稍有不同。

多线程程序的内存要求

AIX 在单个进程中最多支持 32768 个线程。 每个单独的 pthread 需要一些进程地址空间量,所以进程能拥有的 pthread 的实际最大数目取决于内存模型和进程地址空间的使用(为其他目的)。 pthread 需要的内存量包括堆栈大小和防护区域大小,还有一些用于内部使用。 用户可以使用 pthread_attr_setstacksize 子例程控制堆栈的大小,使用 pthread_attr_setguardsize 子例程控制警戒区域的大小。
注: 命令 ulimit -s 对堆栈大小施加的软限制仅适用于应用程序主线程的堆栈。
下表指出使用简单的程序可以在 32 位进程中创建的 pthread 的最大数目,这和使用 NULL pthread 属性在循环中创建 pthread 的最大数目没有区别。 在实际程序中,实际数目取决于程序中其他内存的使用。 对于 64 位进程,ulimit 子例程控制能创建多少线程。 因此不需要大数据模型,而且实际上能够减少线程的最大数目。
数据模型 -bmaxdata 最大 Pthread 数
小数据 不适用 1084
大数据 0x10000000 2169
大数据 0x20000000 4340
大数据 0x30000000 6510
大数据 0x40000000 8681
大数据 0x50000000 10852
大数据 0x60000000 13022
大数据 0x70000000 15193
大数据 0x80000000 17364
可设置 NUM_SPAREVP 环境变量来控制库维护的备用虚拟处理器的数目。 不需要修改此变量。 在某些情况下,仅使用少量兆字节的内存的应用程序能通过将 NUM_SPAREVP 环境变量设为较小的值减少内存开销。 典型设置包括系统上的 CPU 数目或者进程线程的峰值数目。 设置此变量并不影响进程性能。 缺省设为 256。
注: NUM_SPAREVP 环境变量仅在 AIX 5.1中可用。

多线程程序的示例

以下一段较短的多线程程序用英语和法语显示“Hello!” 五秒钟。 使用 cc_rxlc_r 进行编译。 F
#include <pthread.h>    /* include file for pthreads - the 1st */
#include <stdio.h>      /* include file for printf()           */
#include <unistd.h>     /* include file for sleep()            */

void *Thread(void *string)
{
        while (1)
                printf("%s\n", (char *)string);
        pthread_exit(NULL);
}

int main()
{
        char *e_str = "Hello!";
        char *f_str = "Bonjour !";

        pthread_t e_th;
        pthread_t f_th;

        int rc;

        rc = pthread_create(&e_th, NULL, Thread, (void *)e_str);
        if (rc)
                exit(-1);
        rc = pthread_create(&f_th, NULL, Thread, (void *)f_str);
        if (rc)
                exit(-1);
        sleep(5);

        /* usually the exit subroutine should not be used
           see below to get more information */
        exit(0);
}

初始线程(执行 main 例程)创建两个线程。 两个线程有相同的入口点例程(Thread 例程),但是参数不同。 参数是指向要显示的字符串的指针。

调试多线程程序

下面的工具可用于调试多线程程序:
  • 应用程序员可以使用 dbx 命令来执行调试。 有多个子命令可用于显示与线程相关的对象,包括 属性条件mutex线程
  • 内核程序员可以使用内核调试程序在内核扩展和设备驱动程序上进行调试。 内核调试程序提供有限的用户线程访问,并主要处理内核线程。 多个子命令支持多内核线程和处理器,包括:
    • cpu 子命令,用于更改当前处理器
    • ppd 子命令,它显示每个处理器的数据结构
    • thread 子命令,它显示线程表条目
    • uthread 子命令,它显示线程的 uthread 结构
    有关内核调试程序的更多信息,请参阅 内核扩展和设备支持编程概念

多线程程序的核心文件要求

缺省情况下,进程不生成完整的核心文件。 如果应用程序必须调试共享内存区域中的数据(特别是线程堆栈),那么有必要生成完整的核心转储。 要生成完整的核心文件信息,请作为 root 用户运行下面的命令:
	chdev -l sys0 -a fullcore=true

每个单独的 pthread 都会增加生成的核心文件的大小。 pthread 需要的核心文件空间量包括堆栈大小,用户可以使用 pthread_attr_setstacksize 子例程来控制堆栈大小。 对于使用 NULL pthread 属性创建的 pthread,32 位进程中的每一个 pthread 会使核心文件的大小增加 128 KB,64 位进程中的每一个 pthread 会使核心文件的大小增加 256 KB。