开源中文网

您的位置: 首页 > 编程开发 > C++语言编程 > 正文

多线程编程学习6-安全和不安全接口

来源:  作者:

本章定义函数和库的 MT 安全级别。本章论述以下主题:
■    第 165 页中的 “线程安全”
■    第 167 页中的 “MT 接口安全级别”
■    第 168 页中的 “异步信号安全函数”
■    第 169 页中的 “库的 MT 安全级别”
 
线程安全可以避免数据竞争。 不管数据值设置的正确与否,都会出现数据争用的情况,具
体取决于多个线程访问和修改数据的顺序。
不需要共享时,请为每个线程提供一个专用的数据副本。如果共享非常重要,则提供显式
同步,以确保程序以确定的方式操作。
当某个过程由多个线程同时执行时,如果该过程在逻辑上是正确的,则认为该过程是线程
安全的。实际上,一般可分为以下几种安全性级别。
■    不安全
■    线程安全,可串行化
■    线程安全,MT 安全
通过将过程包含在语句中来锁定和解除锁定互斥,可以使不安全过程变成线程安全过程,而且可以进行串行化。示例 6–1 说明了 fputs() 的三个简化实现,最初线程是不安全的。
下面是此例程的可串行化版本,它使用单一互斥来防止过程出现并发执行问题。实际上,单一互斥比通常需要的同步效果更强。当两个线程使用 fputs() 将输出发送到不同文件时,一个线程无需等待另一个线程。只有在共享输出文件时,线程才需要进行同步。
最新版本是 MT 安全的。此版本对每个文件都使用一个锁定,允许两个线程同时指向不同
的文件。因此,只要例程是线程安全的,该例程就是 MT 安全的,而且例程的执行不会对
性能造成负面影响。
 
示例6–1线程安全程度
 
/* not thread-safe */
 
fputs(const char *s, FILE *stream) {
 
char *p;
 
for (p=s; *p; p++)
 
putc((int)*p, stream);
 
}
 
 
 
/* serializable */
 
fputs(const char *s, FILE *stream) {
 
static mutex_t mut;
 
char *p;
 
mutex_lock(&m);
 
for (p=s; *p; p++)
 
putc((int)*p, stream);
 
 
 
mutex_unlock(&m);
 
}
 
 
 
/* MT-Safe */
 
mutex_t m[NFILE];
 
fputs(const char *s, FILE *stream) {
 
static mutex_t mut;
 
char *p;
 
mutex_lock(&m[fileno(stream)]);
 
for (p=s; *p; p++)
 
putc((int)*p, stream);
 
mutex_unlock(&m[fileno(stream)]0;
 
}
 
 
 
 
 
MT 接口安全级别
 
线程手册页 man(3C) 使用表 6–1 中列出的安全级别类别来描述接口对线程的支持程度。这些类别在 Intro(3) 手册页中进行了完整说明。
 
表 6–1接口安全级别
 

类别 说明
安全 可以从多线程应用程序中调用此代码
安全(包含异常) 有关异常的说明,请参见对应手册页的NOTES 部分。
不安全 此接口与多线程应用程序结合使用时是不安全的,除非应用程序一次仅安排在库中执行一个线程
MT 安全 此接口已完全做好准备,可以执行多线程访问。此接口是安全的,而且支持一定的并发性。
MT 安全(包含异常) 有关异常的列表,请参见《man pages section 3: Basic LibraryFunctions》中对应页中的 NOTES 部分。
异步信号安全 可以从信号处理程序中安全地调用此例程。执行异步信号安全例程的线程在被信号中断时,不会自行死锁。
Fork1–安全 每次调用 Solaris fork1(2) 或 POSIX fork(2) 时,此接口都会释放持有的锁定。
 
有关库例程的安全级别,请参见参考手册页的第 3 部分。
出于以下原因,特意未将某些函数设为安全的。
■    设置为 MT 安全的接口对单线程应用程序的性能有负面影响。
■    库具有不安全的接口。例如,函数可能会返回指向栈中缓冲区的指针。可以对其中的某
些函数使用可重复执行的对应函数。可重复执行的函数名称是在原始函数名称后附加"_r"。
 
注意 – 确定名称不以 "_r" 结尾的函数是否是 MT 安全的唯一方法就是查看该函数的手册页。必须使用同步设备或通过限制初始线程来保护对标识为非 MT 安全的函数的使用。
 
不安全接口的可重复执行函数
对于包含不安全接口的大多数函数而言,存在例程的 MT 安全版本。新的 MT 安全例程的名称始终为原有不安全例程的名称附加 "_r" 后的形式。Solaris 环境中提供表 6–2 "_r" 例程。
 
表 6–2
可重复执行函数
 
 
asctime_r(3c)
ctermid_r(3s)
ctime_r(3c)
fgetgrent_r(3c)
fgetpwent_r(3c)
fgetspent_r(3c)
gamma_r(3m)
getauclassent_r(3)
getauclassnam_r(3)
getauevent_r(3)
getauevnam_r(3)
getauevnum_r(3)
getgrent_r(3c)
getgrgid_r(3c)
getgrnam_r(3c)
gethostbyaddr_r(3n)
gethostbyname_r(3n)
gethostent_r(3n)
getlogin_r(3c)
getnetbyaddr_r(3n)
getnetbyname_r(3n)
getnetent_r(3n)
getnetgrent_r(3n)
getprotobyname_r(3n)
getprotobynumber_r(3n)
getprotoent_r(3n)
getpwent_r(3c)
getpwnam_r(3c)
getpwuid_r(3c)
getrpcbyname_r(3n)
getrpcbynumber_r(3n)
getrpcent_r(3n)
getservbyname_r(3n)
getservbyport_r(3n)
getservent_r(3n)
getspent_r(3c)
getspnam_r(3c)
gmtime_r(3c)
lgamma_r(3m)
localtime_r(3c)
nis_sperror_r(3n)
rand_r(3c)
readdir_r(3c)
strtok_r(3c)
tmpnam_r(3s)
ttyname_r(3c)
 
 
可以从信号处理程序中安全调用的函数就是异步信号安全函数。POSIX 标准定义并列出了
异步信号安全函数(IEEE Std 1003.1-1990, 3.3.1.3 (3)(f),第 55 页)。除 POSIX 异步信号安全
函数外,Solaris 线程接口中的以下函数也是异步信号安全函数:
■    sema_post(3C)
■    thr_sigsetmask(3C),类似于 pthread_sigmask(3C)
■    thr_kill(3C),类似于 pthread_kill(3C)
 
库的 MT 安全级别
 
所有可能由线程从多线程程序中调用的例程都应该是 MT 安全的。因此,例程的两项或多
项激活操作必须能够同时正确执行。这样,多线程程序使用的每个库接口都必须是 MT 安
全级别。
目前,并非所有库都是 MT 安全的。表 6–3 中列出了常用的 MT 安全库。其他的库最终会被修改为 MT 安全的。
 
表 6–3 部分 MT 安全的库
 
注释
 
lib/libc 不安全的接口具有 *_r 形式的线程安全接口,通常包含不同的语义。
lib/libdl_stubs
 
支持静态切换编译
lib/libintl
 
国际化库
lib/libm
 
符合 System V Interface De?nition, Edition 3, X/Open and ANSI C 的数学库
lib/libmalloc
 
空间有效内存分配库,请参见malloc(3X)
 
lib/libmapmalloc
 
基于 mmap 的备选内存分配库,请参见 mapmalloc(3X)
 
lib/libnsl
 
TLI 接口、XDR、RPC 客户机和服务器、netdir、netselect 以及
getXXbyYY 接口都不是安全的,但都具有 getXXbyYY_r 形式的线程安全接口
lib/libresolv
 
线程特定 errno 支持
 
lib/libsocket
 
用于执行网络连接的套接字库
lib/libw
 
支持多字节语言环境的宽字符和宽字符串函数
lib/straddr
 
名称到地址的网络转换库
lib/libX11
 
X11 Windows 库例程
lib/libC
 
C++ 运行时共享对象
 
 
不安全库
 
只有在单线程调用时,多进程程序才能安全地调用库中无法保证是 MT 安全级别的例程。

Tags:线程 接口
关于开源中文网 - 联系我们 - 广告服务 - 网站地图 - 版权声明