【发布时间】:2020-04-16 20:41:51
【问题描述】:
来自 Linux 编程接口,在 §31.3.4 Employing the Thread-Specific Data API 中,它给出了使用线程特定数据制作线程不安全函数 thead-safe:
头部不安全版本:
/* Listing 31-1 */
/* strerror.c
An implementation of strerror() that is not thread-safe.
*/
#define _GNU_SOURCE /* Get '_sys_nerr' and '_sys_errlist'
declarations from <stdio.h> */
#include <stdio.h>
#include <string.h> /* Get declaration of strerror() */
#define MAX_ERROR_LEN 256 /* Maximum length of string
returned by strerror() */
static char buf[MAX_ERROR_LEN]; /* Statically allocated return buffer */
char *
strerror(int err)
{
if (err < 0 || err >= _sys_nerr || _sys_errlist[err] == NULL) {
snprintf(buf, MAX_ERROR_LEN, "Unknown error %d", err);
} else {
strncpy(buf, _sys_errlist[err], MAX_ERROR_LEN - 1);
buf[MAX_ERROR_LEN - 1] = '\0'; /* Ensure null termination */
}
return buf;
}
具有线程特定数据的线程安全版本:
/* Listing 31-3 */
/* strerror_tsd.c
An implementation of strerror() that is made thread-safe through
the use of thread-specific data.
See also strerror_tls.c.
*/
#define _GNU_SOURCE /* Get '_sys_nerr' and '_sys_errlist'
declarations from <stdio.h> */
#include <stdio.h>
#include <string.h> /* Get declaration of strerror() */
#include <pthread.h>
#include "tlpi_hdr.h"
static pthread_once_t once = PTHREAD_ONCE_INIT;
static pthread_key_t strerrorKey;
#define MAX_ERROR_LEN 256 /* Maximum length of string in per-thread
buffer returned by strerror() */
static void /* Free thread-specific data buffer */
destructor(void *buf)
{
free(buf);
}
static void /* One-time key creation function */
createKey(void)
{
int s;
/* Allocate a unique thread-specific data key and save the address
of the destructor for thread-specific data buffers */
s = pthread_key_create(&strerrorKey, destructor);
if (s != 0)
errExitEN(s, "pthread_key_create");
}
char *
strerror(int err)
{
int s;
char *buf;
/* Make first caller allocate key for thread-specific data */
s = pthread_once(&once, createKey);
if (s != 0)
errExitEN(s, "pthread_once");
buf = pthread_getspecific(strerrorKey);
if (buf == NULL) { /* If first call from this thread, allocate
buffer for thread, and save its location */
buf = malloc(MAX_ERROR_LEN);
if (buf == NULL)
errExit("malloc");
s = pthread_setspecific(strerrorKey, buf);
if (s != 0)
errExitEN(s, "pthread_setspecific");
}
if (err < 0 || err >= _sys_nerr || _sys_errlist[err] == NULL) {
snprintf(buf, MAX_ERROR_LEN, "Unknown error %d", err);
} else {
strncpy(buf, _sys_errlist[err], MAX_ERROR_LEN - 1);
buf[MAX_ERROR_LEN - 1] = '\0'; /* Ensure null termination */
}
return buf;
}
在本章的总结部分它说:
...
SUSv3 中规定的大部分功能都需要 线程安全的。 SUSv3 还列出了一小组不属于 要求是线程安全的。通常,这些是采用 静态存储将信息返回给调用者或维护 连续呼叫之间的信息。 根据定义,此类函数 是不可重入的,并且不能使用互斥锁来制作它们 线程安全的。我们考虑了两个大致等效的编码 技术——特定于线程的数据和线程本地存储——可以 用于渲染不安全的函数线程安全而无需 改变它的界面。
...
我了解,使用特定于线程的数据旨在将线程不安全函数变为线程安全函数,不更改函数的接口/签名。
但我不明白:
根据定义,这样的函数是不可重入的,不能使用互斥体来制作它们 线程安全的。
问题:
为什么说“不能使用互斥锁......而线程特定的数据可以......”?是否有任何条件可以使线程不安全函数仅使用特定于线程的数据而不使用互斥锁成为线程安全的?
我想我可以将线程不安全的
strerror()设置为线程安全的,只需添加一个互斥锁即可。与使用线程特定数据发布的相比,它有什么不同吗? (可能会损失一些并发效率?因为我要使用互斥锁来锁定访问静态变量的代码)
【问题讨论】:
-
strerror()的“线程安全”版本通过返回一个指针来打破长期建立的strerror()接口,如果调用strerror()的线程退出,该指针将变为无效。跨度>
标签: c multithreading unix thread-safety mutex