12.5. Reentrancy
We discussed reentrant functions and signal handlers in
Section 10.6
. Threads are similar to signal handlers when it comes to reentrancy. With both signal handlers and threads, multiple threads of control can potentially call the same function at the same time.
If a function can be safely called by multiple threads at the same time, we say that the function is
thread-safe
. All functions defined in the Single UNIX Specification are guaranteed to be thread-safe, except those listed in
Figure 12.9
. In addition, the
ctermid
and
tmpnam
functions are not guaranteed to be thread-safe if they are passed a null pointer. Similarly, there is no guarantee that
wcrtomb
and
wcsrtombs
are thread-safe when they are passed a null pointer for their
mbstate_t
argument.
Figure 12.9. Functions
not
guaranteed to be thread-safe by POSIX.1
|
asctime
|
ecvt
|
gethostent
|
getutxline
|
putc_unlocked
|
basename
|
encrypt
|
getlogin
|
gmtime
|
putchar_unlocked
|
catgets
|
endgrent
|
getnetbyaddr
|
hcreate
|
putenv
|
crypt
|
endpwent
|
getnetbyname
|
hdestroy
|
pututxline
|
ctime
|
endutxent
|
getnetent
|
hsearch
|
rand
|
dbm_clearerr
|
fcvt
|
getopt
|
inet_ntoa
|
readdir
|
dbm_close
|
ftw
|
getprotobyname
|
l64a
|
setenv
|
dbm_delete
|
gcvt
|
getprotobynumber
|
lgamma
|
setgrent
|
dbm_error
|
getc_unlocked
|
getprotoent
|
lgammaf
|
setkey
|
dbm_fetch
|
getchar_unlocked
|
getpwent
|
lgammal
|
setpwent
|
dbm_firstkey
|
getdate
|
getpwnam
|
localeconv
|
setutxent
|
dbm_nextkey
|
getenv
|
getpwuid
|
localtime
|
strerror
|
dbm_open
|
getgrent
|
getservbyname
|
lrand48
|
strtok
|
dbm_store
|
getgrgid
|
getservbyport
|
mrand48
|
ttyname
|
dirname
|
getgrnam
|
getservent
|
nftw
|
unsetenv
|
dlerror
|
gethostbyaddr
|
getutxent
|
nl_langinfo
|
wcstombs
|
drand48
|
gethostbyname
|
getutxid
|
ptsname
|
wctomb
|
Implementations that support thread-safe functions will define the
_POSIX_THREAD_SAFE_FUNCTIONS
symbol in
<unistd.h>
. Applications can also use the
_SC_THREAD_SAFE_FUNCTIONS
argument with
sysconf
to check for support of thread-safe functions at runtime. All XSI-conforming implementations are required to support thread-safe functions.
When it supports the thread-safe functions feature, an implementation provides alternate, thread-safe versions of some of the POSIX.1 functions that aren't thread-safe.
Figure 12.10
lists the thread-safe versions of these functions. Many functions are not
thread-safe, because they return data stored in a static memory buffer. They are made thread-safe by changing their interfaces to require that the caller provide its own buffer.
Figure 12.10. Alternate thread-safe functions
acstime_r
gmtime_r
ctime_r
localtime_r
getgrgid_r
rand_r
getgrnam_r
readdir_r
getlogin_r
strerror_r
getpwnam_r
strtok_r
getpwuid_r
ttyname_r
The functions listed in
Figure 12.10
are named the same as their non-thread-safe relatives, but with an
_r
appended at the end of the name, signifying that these versions are reentrant.
If a function is reentrant with respect to multiple threads, we say that it is thread-safe. This doesn't tell us, however, whether the function is reentrant with respect to signal handlers. We say that a function that is safe to be reentered from an asynchronous signal handler is
async-signal safe
. We saw the async-signal safe functions in
Figure 10.4
when we discussed reentrant functions in
Section 10.6
.
In addition to the functions listed in
Figure 12.10
, POSIX.1 provides a way to manage
FILE
objects in a thread-safe way. You can use
flockfile
and
ftrylockfile
to obtain a lock associated with a given
FILE
object. This lock is recursive: you can acquire it again, while you already hold it, without deadlocking. Although the exact implementation of the lock is unspecified, it is required that all standard I/O routines that manipulate
FILE
objects behave as if they call
flockfile
and
funlockfile
internally.
Although the standard I/O routines might be implemented to be thread-safe from the perspective of their own internal data structures, it is still useful to expose the locking to applications. This allows applications to compose multiple calls to standard I/O functions into atomic sequences. Of course, when dealing with multiple
FILE
objects, you need to beware of potential deadlocks and to order your locks carefully.
If the standard I/O routines acquire their own locks, then we can run into serious performance degradation when doing character-at-a-time I/O. In this situation, we end up acquiring and releasing a lock for every character read or written. To avoid this overhead, unlocked versions of the character-based standard I/O routines are available.
#include <stdio.h>
int getchar_unlocked(void);
int getc_unlocked(FILE *fp);
|
Both return: the next character if OK,
EOF
on end of file or error
|
int putchar_unlocked(int c);
int putc_unlocked(int c, FILE *fp);
|
Both return:
c
if OK,
EOF
on error
|
These four functions should not be called unless surrounded by calls to
flockfile
(or
ftrylockfile
) and
funlockfile
. Otherwise, unpredictable results can occur (i.e., the types of problems that result from unsynchronized access to data by multiple threads of control).
Once you lock the
FILE
object, you can make multiple calls to these functions before releasing the lock. This amortizes the locking overhead across the amount of data read or written.
Example
Figure 12.11
shows a possible implementation of
getenv
(
Section 7.9
). This version is not reentrant. If two threads call it at the same time, they will see inconsistent results, because the string returned is stored in a single static buffer that is shared by all threads calling
getenv
.
We show a reentrant version of
getenv
in
Figure 12.12
. This version is called
getenv_r
. It uses the
pthread_once
function (described in
Section 12.6
) to ensure that the
thread_init
function is called only once per process.
To make
getenv_r
reentrant, we changed the interface so that the caller must provide its own buffer. Thus, each thread can use a different buffer to avoid interfering with the others. Note, however, that this is not enough to make
getenv_r
thread-safe. To make
getenv_r
thread-safe, we need to protect against changes to the environment while we are searching for the requested string. We can use a mutex to serialize access to the environment list by
getenv_r
and
putenv
.
We could have used a readerwriter lock to allow multiple concurrent calls to
getenv_r
, but the added concurrency probably wouldn't improve the performance of our program by very much, for two reasons. First, the environment list usually isn't very long, so we won't hold the mutex for too long while we scan the list. Second, calls to
getenv
and
putenv
are infrequent, so if we improve their performance, we won't affect the overall performance of the program very much.
If we make
getenv_r
thread-safe, that doesn't mean that it is reentrant with respect to signal handlers. If we use a nonrecursive mutex, we run the risk that a thread will deadlock itself if it calls
getenv_r
from a signal handler. If the signal handler interrupts the thread while it is executing
getenv_r
, we will already be holding
env_mutex
locked, so another attempt to lock it will block, causing the thread to deadlock. Thus, we must use a recursive mutex to prevent other threads from changing the data structures while we look at them, and also prevent deadlocks from signal handlers. The problem is that the pthread functions are not guaranteed to be async-signal safe, so we can't use them to make another function async-signal safe.
Figure 12.11. A nonreentrant version of
getenv
#include <limits.h>
#include <string.h>
static char envbuf[ARG_MAX];
extern char **environ;
char *
getenv(const char *name)
int i, len;
len = strlen(name);
for (i = 0; environ[i] != NULL; i++) {
if ((strncmp(name, environ[i], len) == 0) &&
(environ[i][len] == '=')) {
strcpy(envbuf, &environ[i][len+1]);
return(envbuf);
return(NULL);
Figure 12.12. A reentrant (thread-safe) version of
getenv
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
extern char **environ;
pthread_mutex_t env_mutex;
static pthread_once_t init_done = PTHREAD_ONCE_INIT;
static void
thread_init(void)
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&env_mutex, &attr);
pthread_mutexattr_destroy(&attr);
getenv_r(const char *name, char *buf, int buflen)
int i, len, olen;
pthread_once(&init_done, thread_init);
len = strlen(name);
pthread_mutex_lock(&env_mutex);
for (i = 0; environ[i] != NULL; i++) {
if ((strncmp(name, environ[i], len) == 0) &&
(environ[i][len] == '=')) {
olen = strlen(&environ[i][len+1]);
if (olen >= buflen) {
pthread_mutex_unlock(&env_mutex);
return(ENOSPC);
strcpy(buf, &environ[i][len+1]);
pthread_mutex_unlock(&env_mutex);
return(0);
pthread_mutex_unlock(&env_mutex);
return(ENOENT);
12.5. ReentrancyWe discussed reentrant functions and signal handlers in Section 10.6. Threads are similar to signal handlers when it comes to reentrancy. With both
今天研究下线程安全函数和非线程安全函数。什么是线程安全函数?我们知道在多线程编程中,线程安全问题是不容忽视的。只要存在多线程,就会存在多个线程访问同一段代码或者同一个全局变量的临界区,对于uc中标准函数也是一样(类似于windows中的原子函数)。当多个线程同时调用一个标准函数时,执行同一段代码,同样会在函数内部形成临界区,就可能出现问题。
线程安全函数就是针对上述问题,在函数内
2.1 介绍
List接口继承自Collection接口(单列集合,用来存储一个一个的对象)。其特点是可以存储有序,可重复的数据。是一种动态的数组。
继承自List的三个常用集合类:
ArrayList
LinkedList
Vector
插播一经典面试题-上面三者的区别:
ArrayList:作为List...
C 中大多数缓冲区溢出问题可以直接追溯到标准 C 库。最有害的罪魁祸首是不进行自变量检查的、有问题的字符串操作(strcpy、strcat、sprintf 和 gets)。一般来讲,象“避免使用 strcpy()”和“永远不使用 gets()”这样严格的规则接近于这个要求。
今天,编写的程序仍然利用这些调用,因为从来没有人教开发人员避免使用它们。某些人从各处获得某个提示,但即使
一、DESCRIPTION
This tutorial describes the use of Perl interpreter threads (sometimes referred to as ithreads)
that was first introduced in Perl 5.6.0.
Spring框架本身并不直接保证线程安全,但它提供了一些机制来帮助开发者编写线程安全的代码。
其中最重要的机制是通过控制对象的作用域来保证线程安全。Spring中的默认作用域是singleton,也就是说每个Bean只有一个实例。这种单例模式可以在多个线程中共享,因此Spring容器负责管理这些对象的实例化和销毁,并确保它们在运行时是线程安全的。当然,如果需要,Spring也提供了其他作用域,如prototype和request等,以满足不同的需求。
此外,Spring还提供了一些注解和工具类,如@ThreadSafe和ConcurrentHashMap等,来帮助开发者编写线程安全的代码。例如,@ThreadSafe注解可以标记一个Bean是线程安全的,Spring容器就会采取相应的措施来确保它的线程安全性。而ConcurrentHashMap则是一个高效的线程安全的哈希表,可以在多个线程中并发地读写。
总之,虽然Spring框架本身并不直接保证线程安全,但通过它提供的作用域、注解和工具类等机制,开发者可以更方便地编写线程安全的代码。