相关文章推荐
眼睛小的木耳  ·  一文看懂 MySQL ...·  4 月前    · 
坐怀不乱的山羊  ·  text-generation-webui ...·  6 月前    · 
精明的核桃  ·  MongoDB(13)- ...·  11 月前    · 

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.

#include <stdio.h>
int ftrylockfile(FILE *fp);

Returns: 0 if OK, nonzero if lock can't be acquired

void flockfile(FILE *fp);
void funlockfile(FILE *fp);


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框架本身并不直接保证线程安全,但通过它提供的作用域、注解和工具类等机制,开发者可以更方便地编写线程安全的代码。