多线程编程指南

计时器、报警与剖析

在 Solaris 2.5 发行版中,已针对每 LWP 计时器和每线程报警声明了“生命周期结束时间”。请参见 timer_create(3RT)、alarm(2) 或 setitimer(2) 手册页。现在,每 LWP 计时器和每线程报警都被替换为每进程变体,这些内容在本节中加以介绍。

最初,每个 LWP 都有唯一的实时时间间隔计时器和报警,与 LWP 绑定的线程可以使用该计时器和报警。当计时器或报警过期时,会向线程传送一个信号。

每个 LWP 还有一个虚拟时间计时器或配置文件时间间隔计时器,与 LWP 绑定的线程可以使用这些计时器。当时间间隔计时器过期时,系统会根据需要将 SIGVTALRMSIGPROF 发送到拥有该时间间隔计时器的 LWP。

每 LWP POSIX 计时器

在 Solaris 2.3 和 2.4 发行版中,timer_create(3RT) 函数返回一个包含计时器 ID 的计时器对象,该 ID 仅在调用 LWP 中有意义。到期信号将被传送到该 LWP。由于返回的计数器对象的行为,只有绑定线程可以使用 POSIX 计时器工具。

即使使用受到限制,Solaris 2.3 和 2.4 发行版中多线程应用程序的 POSIX 计时器也不可靠。这些计时器不能可靠地屏蔽生成的信号,也不能可靠地传送 sigvent 结构中的关联值。

在 Solaris 2.5 发行版中引入的应用程序可以创建每进程计时器。编译应用时定义了 _POSIX_PER_PROCESS_TIMERS 宏,或通过使用大于或等于 199506L 的值定义宏 _POSIX_C_SOURCE 来编译应用程序。

从 Solaris 9 发行版起生效,所有的计时器都针对每个进程,但虚拟时间计时器和配置文件时间间隔计时器除外,它们仍然针对每个 LWP。有关 ITIMER_VIRTUALITIMER_PROF,请参见 setitimer(2)。

每进程计时器的计时器 ID 在任何 LWP 中都可用。系统将针对进程(而非针对特定 LWP)生成到期信号。

只能通过 timer_delete(3RT) 或在进程终止时删除每进程计时器。

每线程报警

在 Solaris 2.3 和 2.4 发行版中,对 alarm(2) 或 setitimer(2) 的调用仅在调用 LWP 中有意义。LWP 创建终止时,将自动删除这类计时器。由于此行为,只有 alarm()setitimer() 可以使用绑定线程。

即使限于使用绑定线程,Solaris 2.3 和 2.4 多线程应用程序中的 alarm()setitimer() 计时器也不可靠。特别是,在从发出这些调用的绑定线程屏蔽信号方面,alarm()settimer() 计时器不可靠。如果不需要这类屏蔽,则这两个系统调用可在绑定线程中可靠地工作。

在 Solaris 2.5 发行版中,调用 alarm() 时,与 -lpthread (POSIX) 线程链接的应用程序将获取每个进程传送的 SIGALRMalarm() 生成的 SIGALRM是针对进程生成,而不是针对特定 LWP 生成。另外,进程终止时,将重置报警。

使用 Solaris 2.5 之前的发行版编译的应用程序或没有与 -lpthread 链接的应用程序将继续查看每个 LWP 传送的信号,这些信号是由 alarm()setitimer() 生成的。

alarm()setitimer(ITIMER_REAL) 的调用将导致生成的 SIGALRM 信号被发送到进程(从 Solaris 9 发行版开始生效)。

剖析多线程程序

在 Solaris 2.6 以前的 Solaris 发行版中,在多线程程序中调用 profil() 仅影响调用 LWP。创建 LWP 时不会继承配置文件状态。要使用全局配置文件缓冲区来配置多线程程序,线程启动时每个线程都需要调用 profil()。此外,每个线程必须为绑定线程。这些限制很麻烦。它们不能顺利支持动态打开和关闭剖析。

在 Solaris 2.6 以及更高发行版中,对多线程进程的 profil() 系统调用具有全局影响力。对 profil() 的调用将影响进程中的所有 LWP 和线程。profil() 可能会使与以前的每 LWP 语义相关的应用程序中断。但是,预计调用 profil() 可以改进需要在运行时动态打开和关闭剖析的多线程程序。