Writing Reentrant and Thread-Safe Code(译:编写可重入和线程安全的代码)

Writing Reentrant and Thread-Safe Code


编写可重入和线程安全的代码

(http://www.ualberta.ca/dept/chemeng/AIX-43/share/man/info/C/a_doc_lib/aixprggd/genprogc/writing_reentrant_thread_safe_code.htm)

In single-threaded processes there is only one flow of control. The code
executed by these processes thus need not to be reentrant or thread-safe. In
multi-threaded programs, the same functions and the same resources may be
accessed concurrently by several flows of control. To protect resource
integrity, code written for multi-threaded programs must be reentrant and
thread-safe.
单线程的进程中仅有一个控制流。这种进程执行的代码无需可重入或线程安全。在多线程的程序中,同一函数或资源可能被多个控制流并发访问。为保护资源完整性,多线程程序编码必须可重入且线程安全。

This section provides information for writing reentrant and thread-safe
programs. It does not cover the topic of writing thread-efficient programs.
Thread-efficient programs are efficiently parallelized programs. This can only
be done during the design of the program. Existing single-threaded programs can
be made thread-efficient, but this requires that they be completely redesigned
and rewritten.
本节提供了一些编写可重入和线程安全程序的(指导)信息,但不包括编写线程高效程序的主题。线程高效程序是高效并行化的程序,仅可在程序设计中实现。现有的单线程程序可变得线程高效,但这需要完全地重新设计和重写。

?

Understanding Reentrance and Thread-Safety

Reentrance and thread-safety are both related to the way functions handle
resources. Reentrance and thread-safety are separate concepts: a function can be
either reentrant, thread-safe, both, or neither.
可重入和线程安全与函数处理资源的方式有关。可重入和线程安全是两个相互独立的概念:一个函数可以仅是可重入的,可以仅是线程安全的,可以两者皆是或两者皆不是。

Reentrance

A reentrant function does not hold static data over successive calls, nor
does it return a pointer to static data. All data is provided by the caller of
the function. A reentrant function must not call non-reentrant functions.
可重入函数不能为后续的调用保持静态(或全局)数据,也不能返回指向静态(或全局)数据的指针。函数中用到的所有数据,都应由函数调用者提供(不包括栈上的局部数据)。可重入函数不能调用不可重入的函数。

A non-reentrant function can often, but not always, be identified by its
external interface and its usage. For example, the strtok subroutine is not
reentrant, because it holds the string to be broken into tokens. The ctime
subroutine is also not reentrant; it returns a pointer to static data that is
overwritten by each call.
不可重入的函数经常(但不总是)可以通过其外部接口和用法识别。例如strtok子例程是不可重入的,因为它保存着将被分隔为子串的字符串。ctime也是不可重入的,它返回一个指向静态数据的指针,每次调用都会覆盖这些数据。

Thread-Safety

A thread-safe function protects shared resources from concurrent access by
locks. Thread-safety concerns only the implementation of a function and does not
affect its external interface.
线程安全的函数通过“锁”来保护共享资源不被并发地访问。“线程安全”仅关心函数的实现,而不影响其外部接口。

In C, local variables are dynamically allocated on the stack. Therefore, any
function that does not use static data or other shared resources is trivially
thread-safe. For example, the following function is thread-safe:
在C中,局部变量在栈上动态分配,因此,任何不使用静态数据和其它共享资源的函数就是最普通的线程安全(函数)。例如,以下函数就是线程安全的:

1 /* thread-safe function */
2 int diff(int x, int y)
3 {
4 int delta;
5 delta = y - x;
6 if (delta < 0)
7 delta = -delta;
8 return delta;
9 }

The use of global data is thread-unsafe. It should be maintained per thread
or encapsulated, so that its access can be serialized. A thread may read an
error code corresponding to an error caused by another thread. In AIX, each
thread has its own errno value.
使用全局数据是线程不安全的。应为每个线程维护一份全局数据的拷贝或封装全局数据,以使对它的访问变成串行的。线程可能读取另一线程造成的错误对应的错误码。在AIX系统中,每个线程拥有属于自己的错误码(errno)值。

Making a Function Reentrant


In most cases, non-reentrant functions must be replaced by functions with a
modified interface to be reentrant. Non-reentrant functions cannot be used by
multiple threads. Furthermore, it may be impossible to make a non-reentrant
function thread-safe.
在大部分情况下,不可重入的函数修改为可重入函数时,必须修改函数的对外接口。不可重入的函数不能用于多线程。此外,也许不可能让某个不可重入的函数是线程安全的。

Returning Data

Many non-reentrant functions return a pointer to static data. This can be
avoided in two ways:

很多不可重入的函数返回一个指向静态数据的指针。这可通过两种方法避免:

  • Returning dynamically allocated data. In this case, it will be the
    caller‘s responsibility to free the storage. The benefit is that the interface
    does not need to be modified. However, backward compatibility is not ensured;
    existing single-threaded programs using the modified functions without changes
    would not free the storage, leading to memory leaks.
    返回从堆中动态分配的数据(即内存空间地址)。在这种情况下,调用者负责释放堆中的存储空间。其优点是不必修改函数的外部接口,但不能保证向后兼容。现有的单线程程序若不修改而直接使用修改后的函数,将不会释放存储空间,进而导致内存泄露。

  • Using caller-provided storage. This method is recommended, although the
    interface needs to be modified. 由调用者提供存储空间。尽管函数的外部接口需要改变,仍然推荐使用这种方法。

For example, a strtoupper function, converting a string to
uppercase, could be implemented as in the following code fragment:
例如,将字符串转换为大写的strtoupper函数实现可能如下代码片段所示:

 1 /* non-reentrant function */
2 char *strtoupper(char *string)
3 {
4 static char buffer[MAX_STRING_SIZE];
5 int index;
6
7 for (index = 0; string[index]; index++)
8 buffer[index] = toupper(string[index]);
9 buffer[index] = 0;
10
11 return buffer;
12 }

This function is not reentrant (nor thread-safe). Using the first method to
make the function reentrant, the function would be similar to the following code
fragment: 该函数既不是可重入的,也不是线程安全的。使用第一种方法将其改写为可重入的,函数将类似于如下代码片段:

 1 /* reentrant function (a poor solution) */
2 char *strtoupper(char *string)
3 {
4 char *buffer;
5 int index;
6
7 /* error-checking should be performed! */
8 buffer = malloc(MAX_STRING_SIZE);
9
10 for (index = 0; string[index]; index++)
11 buffer[index] = toupper(string[index]);
12 buffer[index] = 0;
13
14 return buffer;
15 }

A better solution consists of modifying the interface. The caller must
provide the storage for both input and output strings, as in the following code
fragment: 更好的解决方案是修改接口。调用者须为输入和输出字符串提供存储空间,如下代码片段所示:

 1 /* reentrant function (a better solution) */
2 char *strtoupper_r(char *in_str, char *out_str)
3 {
4 int index;
5
6 for (index = 0; in_str[index]; index++)
7 out_str[index] = toupper(in_str[index]);
8 out_str[index] = 0;
9
10 return out_str;
11 }

The non-reentrant standard C library subroutines were made reentrant using
the second method. This is discussed below .
通过使用第二种方法,不可重入的C标准库子例程被改写为可重入的。见下文讨论。

Keeping Data over Successive Calls

No data should be kept over successive calls, because different threads may
successively call the function. If a function needs to maintain some data over
successive calls, such as a working buffer or a pointer, this data should be
provided by the caller.
(可重入函数)不应为后续调用保持数据,因为不同线程可能相继调用同一函数。若函数需要在连续调用期间维持某些数据,如工作缓存区或指针,则该数据(资源)应由调用方函数提供调用者应该提供。

Consider the following example. A function returns the successive lowercase
characters of a string. The string is provided only on the first call, as with
the strtok subroutine. The function returns 0 when it reaches the end of the
string. The function could be implemented as in the following code fragment:
考虑如下示例。函数返回字符串中的连续的小写字符。字符串仅在第一次调用时提供,类似strtok子例程。当遍历至字符串末尾时,函数返回0。函数实现可能如下代码片段所示:

 1 /* non-reentrant function */
2 char lowercase_c(char *string)
3 {
4 static char *buffer;
5 static int index;
6 char c = 0;
7
8 /* stores the string on first call */
9 if (string != NULL) {
10 buffer = string;
11 index = 0;
12 }
13
14 /* searches a lowercase character */
15 for (; c = buffer[index]; index++) {
16 if (islower(c)) {
17 index++;
18 break;
19 }
20 }
21 return c;
22 }

This function is not reentrant. To make it reentrant, the static data, the
index variable, needs to be maintained by the caller. The reentrant version of
the function could be implemented as in the following code fragment:
该函数是不可重入的。为使它可重入,静态数据(即index变量)需由调用者来维护。该函数的可重入版本实现可能如下代码片段所示:

/* reentrant function */
char reentrant_lowercase_c(char *string, int *p_index)
{
char c = 0;

/* no initialization - the caller should have done it */

/* searches a lowercase character */
for (; c = string[*p_index]; (*p_index)++) {
if (islower(c)) {
(*p_index)++;
break;
}
}
return c;
}

The interface of the function changed and so did its usage. The caller must
provide the string on each call and must initialize the index to 0 before the
first call, as in the following code fragment:
函数的接口和用法均发生改变。调用者每次调用时必须提供该字符串,并在首次调用前将索引(index)初始化为0,如下代码片段所示:

1 char *my_string;
2 char my_char;
3 int my_index;
4 ...
5 my_index = 0;
6 while (my_char = reentrant_lowercase_c(my_string, &my_index)) {
7 ...
8 }

View
Code

Making a Function Thread-Safe


In multi-threaded programs, all functions called by multiple threads must be
thread-safe. However, there is a workaround for using thread unsafe subroutines
in multi-threaded programs. Note also that non-reentrant functions usually are
thread-unsafe, but making them reentrant often makes them thread-safe, too.
在多线程程序中,所有被多线程调用的函数都必须是线程安全的。然而,在多线程程序中可变通地使用线程不安全的子例程。注意,不可重入的函数通常都是线程不安全的,但将其改写为可重入时,一般也会使其线程安全。

Locking Shared Resources

Functions that use static data or any other shared resources, such as files
or terminals, must serialize the access to these resources by locks in order to
be thread-safe. For example, the following function is thread-unsafe:
使用静态数据或其它任何共享资源(如文件或终端)的函数,必须对这些资源加“锁”来串行访问,以使该函数线程安全。例如,以下函数是线程不安全的:

1 /* thread-unsafe function */
2 int increment_counter()
3 {
4 static int counter = 0;
5
6 counter++;
7 return counter;
8 }

To be thread-safe, the static variable counter needs to be
protected by a static lock, as in the following (pseudo-code) example:
为使该函数线程安全,静态变量counter需要被静态锁保护,如下例(伪代码)所示:

/* pseudo-code thread-safe function */
int increment_counter();
{
static int counter = 0;
static lock_type counter_lock = LOCK_INITIALIZER;

lock(counter_lock);
counter++;
unlock(counter_lock);
return counter;
}

In a multi-threaded application program using the threads library, mutexes
should be used for serializing shared resources. Independent libraries may need
to work outside the context of threads and, thus, use other kinds of locks.
在使用线程库的多线程应用程序中,应使用信号量互斥锁(mutex)来串行访问共享资源,独立库可能需要工作于线程上下文之外,因此使用其他类型的锁。

A Workaround for Thread-Unsafe Functions

It is possible to use thread-unsafe functions called by multiple threads
using a workaround. This may be useful, especially when using a thread-unsafe
library in a multi-threaded program, for testing or while waiting for a
thread-safe version of the library to be available. The workaround leads to some
overhead, because it consists of serializing the entire function or even a group
of functions.
多线程变通地调用线程不安全函数是可能的。这在多线程程序使用线程不安全库时尤其有用,如用于测试或待该库的线程安全版本可用时再予以替换。该变通方案会带来一些开销,因为需对整个函数甚至一组函数进行串行化。

  • ?Use a global lock for the library, and lock it each time you use the
    library (calling a library routine or using a library global variable), as in
    the following pseudo-code fragments:
    对该库使用全局锁,每次使用库(调用库内子例程或使用库内全局变量)时均对其加锁,如下伪代码片段所示:

1     /* this is pseudo-code! */
2 lock(library_lock);
3 library_call();
4 unlock(library_lock);
5
6 lock(library_lock);
7 x = library_var;
8 unlock(library_lock);

This solution can create performance bottlenecks because only one thread can
access any part of the library at any given time. The solution is acceptable
only if the library is seldom accessed, or as an initial, quickly implemented
workaround.
该方案可能产生性能瓶颈,因为任一时刻仅有一个线程可访问库的任一部分。仅当不常访问库,或作为初步快速实现的权宜之计时可以采用该方案。

  • ?Use a lock for each library component (routine or global variable) or
    group of components, as in the following pseudo-code fragments:
    对每个库组件(例程或全局变量)或一组组件使用锁,如下例伪代码片段所示:

1     /* this is pseudo-code! */
2 lock(library_moduleA_lock);
3 library_moduleA_call();
4 unlock(library_moduleA_lock);
5
6 lock(library_moduleB_lock);
7 x = library_moduleB_var;
8 unlock(library_moduleB_lock);

View
Code

This solution is somewhat more complicated to implement than the first one,
but it can improve performance. 该方案实现相比前者稍微复杂,但可提高性能。

Because this workaround should only be used in application programs and not
in libraries, mutexes can be used for locking the library.
该方案应仅用于应用程序而非库,故可用互斥锁对库加锁。

Reentrant and Thread-Safe Libraries


Reentrant and thread-safe libraries are useful in a wide range of parallel
(and asynchronous) programming environments, not just within threads. Thus it is
a good programming practice to always use and write reentrant and thread-safe
functions.
可重入和线程安全库广泛应用于并行(和异步)编程环境,而不仅仅用于线程内。因此,总是使用和编写可重入和线程安全的函数是良好的编程实践。

Using Libraries

Several libraries shipped with the AIX Base Operating System are thread-safe.
In the current version of AIX, the following libraries are thread-safe:
AIX基本操作系统附带的几个代码库是线程安全的。在AIX当前版本中,以下库是线程安全的。

  • ?   Standard C library (libc.a)
     C标准函数库

  • ?   Berkeley compatibility library (libbsd.a).
    BSD兼容函数库

Some of the standard C subroutines are non-reentrant, such as the ctime and
strtok subroutines. The reentrant version of the subroutines have the name of
the original subroutine with a suffix _r (underscore r).
某些标准C子例程是不可重入的,如ctime和strtok子例程。它们的可重入版本函数名是原始子例程名添加"_r"后缀。

When writing multi-threaded programs, the reentrant versions of subroutines
should be used instead of the original version. For example, the following code
fragment: 在编写多线程程序时,应使用子例程的可重入版本来替代原有版本。例如,以下代码片段:

1 token[0] = strtok(string, separators);
2 i = 0;
3 do {
4 i++;
5 token[i] = strtok(NULL, separators);
6 } while (token[i] != NULL);

should be replaced in a multi-threaded program by the following code
fragment: 在多线程程序中应替换为以下代码片段:

1 char *pointer;
2 ...
3 token[0] = strtok_r(string, separators, &pointer);
4 i = 0;
5 do {
6 i++;
7 token[i] = strtok_r(NULL, separators, &pointer);
8 } while (token[i] != NULL);

Thread-unsafe libraries may be used by only one thread in a program. The
uniqueness of the thread using the library must be ensured by the programmer;
otherwise, the program will have unexpected behavior, or may even crash.
线程不安全库可用于单线程程序中。程序员必须确保使用该库的线程唯一性;否则,程序行为不可预料,甚至可能崩溃。

Converting Libraries

This information highlights the main steps in converting an existing library
to a reentrant and thread-safe library. It applies only to C language libraries.
以下信息突出了将现有库转换为可重入和线程安全库的主要步骤(仅适用于C语言代码库)。

  • Identifying exported global variables. Those variables are usually defined
    in a header file with the export keyword.
    识别对外的全局变量。这些变量通常在头文件中用export关键字定义*

【译者注:应为”declared in a header file with the extern keyword”】

Exported global variables should be encapsulated.
The variable should be made private (defined with the static keyword in the
library source code). Access (read and write) subroutines should be created.
应封装对外的全局变量。该变量应改为私有(在库源代码内用static关键字定义)。应创建(读写)该变量的子例程。

  • Identifying static variables and other shared resources. Static variables
    are usually defined with the static keyword.
    识别静态变量和其他共享资源。静态变量通常用static关键字定义。

Locks should be associated with any shared
resource. The granularity of the locking, thus choosing the number of locks,
impacts the performance of the library. To initialize the locks, the one-time
initialization facility may be used.
任一共享资源均应与锁关联。锁的粒度及数目会影响库的性能。可使用”一次性初始化”特性(如pthread_once)来方便地初始化锁。

  • Identifying non-reentrant functions and making them reentrant. See Making
    a Function Reentrant. 识别不可重入函数并使之变为可重入函数。见”编写可重入函数“。

  • Identifying thread-unsafe functions and making them thread-safe. See Making
    a Function Thread-Safe. 识别线程不安全函数并使之变为线程安全函数。见使函数成为线程安全函数。

时间: 2024-10-07 09:04:43

Writing Reentrant and Thread-Safe Code(译:编写可重入和线程安全的代码)的相关文章

Use Reentrant Functions for Safer Signal Handling(译:使用可重入函数进行更安全的信号处理)

Use Reentrant Functions for Safer Signal Handling 使用可重入函数进行更安全的信号处理 How and when to employ reentrancy to keep your code bug free 何时及如何利用可重入性避免代码缺陷 Dipak Jha (mailto:[email protected]?subject=Use reentrant functions for safer signal handling&[email pr

Thread Safe 和 Non Thread Safe 的选择?

首先,Thread Safe 是指程序在运行时需对线程(thread)进行安全检查,以防止有新要求就启动新线程的 CGI 执行方式耗尽系统资源.None Thread Safe 则指程序在运行时不对线程进行安全检查. 再来看 PHP 的两种服务模式:ISAPI 和 FastCGI .ISAPI 服务模式是以 DLL 动态库的形式被调用,可以在被用户请求后执行,在处理完一个用户请求后不会马上消失,所以需要进行线程安全检查,这样来提高程序的执行效率.而 FastCGI 执行方式则是以单一线程来执行操

函数可重入性及编写规范

一.可重入函数1)什么是可重入性?可重入(reentrant)函数可以由多于一个任务并发使用,而不必担心数据错误.相反, 不可重入(non-reentrant)函数不能由超过一个任务所共享,除非能确保函数的互斥(或者使用信号量,或者在代码的关键部分禁用中断).可重入函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据.可重入函数要么使用本地变量,要么在使用全局变量时保护自己的数据. 2)可重入函数:不为连续的调用持有静态数据. 不返回指向静态数据的指针:所有数据都由函数的调用者提供. 使用本地

函数可重入问题reentrant functions(函数执行过程中可以被中断,允许多个副本)

最近经常听到这个名词,以前也听到过,不过接触更多的是“线程安全问题”,而且本人也一直理解的是两个名字的含义是一样的.今天仔细总结一下这个名词相关的概念. 引用博文:可重入函数和不可重入函数 (http://www.cppblog.com/franksunny/archive/2007/08/03/29269.html) 主要用于多任务环境中, 一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误

可重入函数与不可重入函数概念以及编写规范

1.定义 一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误:而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的. 换句话说,我们也可以这样理解,重入即表示重复进入,首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括static),这样的函数就是 purecod

PHP5.3中关于VC9和VC6以及Thread Safe和Non Thread Safe版本选择的问题

转自:http://www.htmer.com/article/716.htm 最近在PHP官网上看到又有新版的PHP下载了,于是上去找找For Windows的版本,可是一看确傻眼了,一共给了四个版本,VC9 x86 Non Thread Safe.VC9 x86 Thread Safe.VC6 x86 Non Thread Safe.VC6 x86 Thread Safe,这让我这个菜鸟头疼啊,还好PHP官网提供下载的地方左边有个英文choose我看懂了,我估摸着就是如何来选择版本的意思吧,

Windows下PHP(Thread Safe与Non Thread Safe)版本说明

转载"http://www.taoz11.com/archives/300.html" linux下直接下载源码,在服务器上编译即可,发现windows下有4个版本: VC9 x86 Non Thread SafeVC9 x86 Thread SafeVC6 x86 Non Thread SafeVC6 x86 Thread Safe 网上查看了下4种版本对应使用的情况 一.如何选择 php5.3 的 VC9 版本和 VC6 版本VC6 版本是使用 Visual Studio 6 编译

PHP Thread Safe和Non ThreadSafe

原文地址 http://blog.csdn.net/lrcoop/article/details/45477371 如何选择Thread Safe和Non ThreadSafe版本? Windows版的PHP从版本5.2.1开始有ThreadSafe(线程安全)和None Thread Safe(NTS,非线程安全)之分,这两者不同在于何处?到底应该用哪种?这里做一个简单的介绍. 从2000年10月20日发布的第一个Windows版的PHP3.0.17开始的都是线程安全的版本,这是由于与linu

php Thread Safe(线程安全)与None Thread Safe(NTS,非线程安全)之分

Windows版的PHP从版本5.2.1开始有Thread Safe(线程安全)和None Thread Safe(NTS,非线程安全)之分,这两者不同在于何处?到底应该用哪种?这里做一个简单的介绍. 从2000年10月20日发布的第一个Windows版的PHP3.0.17开始的都是线程安全的版本,这是由于与Linux/Unix系统是采用多进程的工作方式不同的是Windows系统是采用多线程的工作方式.如果在IIS下以CGI方式运行PHP会非常慢,这是由于CGI模式是建立在多进程的基础之上的,而