1 对于C语言的函数来说
The terms reentrant and thread-safe are used to specify how a function can be used in multithreaded applications:
****A reentrant function can be called simultaneously by multiple threads provided that each invocation of the function references unique data.
****A thread-safe function can be called simultaneously by multiple threads when each invocation references shared data. All access to the shared data is serialized.
在C语言的时代,可重入函数是说当一个函数被不同的线程同时访问的时候,每一个调用都独自使用自己的一份数据,多次调用之间不存在数据共享。所以说C语言可重入的函数一定是线程安全的,因为根本就不存在多个线程共享一份数据问题,就不会发生访问冲突。可重入函数的要求是函数不访问全局或静态变量。
线程安全如果多个线程对同一个函数的多次调用之间存在共享数据,所有对共享数据的访问都必须是序列化的,每个线程按先来后到排队访问,也就是不存在同时访问。就像做事情大家都排好了对就不会乱,而各自争抢就容易出错。
2 对于 C++ 的类来说
By extension, a class is said to be reentrant if each and every one of its functions can be called simultaneously by multiple threads on different instances of the class. Similarly, the class is said to be thread-safe if the functions can be called by different threads on the same instance.
但是到了C++ 时代,引入了类的概念,也就引入了类成员函数的线程安全和线程可重入的概念。这个概念是对普通函数的线程安全和线程可重入的扩展。你也可以认为是另外的一对概念。
类的成员函数可重入是说:类的成员函数在被多个线程在不同实例里面访问(每个线程一个实例)时,不存在共享数据,也不会有数据冲突。
类的可重入表示类的所有成员函数都是可重入的。
类的线程安全是说:类的同一个实例被多个线程访问的时候,对数据的操纵是序列化的,不会有访问冲突。
3 注解
要想彻底理解这几个概念,就不得不搞清楚C/C++的几种变量类型。
全局变量 --- 生命周期同进程生命周期,可见域是全局
静态变量 --- 生命周期同进程生命周期,可见域是定义变量的域
成员变量 --- 生命周期同类的实例,可见域是类内部(外部只能间接调用,或根本无法调用)
临时变量 --- 生命周期在定义变量的域内,可见域是定义变量的域
上面所说两类可重入函数的共同点是函数不可访问静态或者全局变量。但是由于C 语言没有成员变量,所以可重入函数只会访问临时变量,多个线程对同一个函数的两个调用绝不会有数据共享,也就不存在访问冲突,所以C语言类型的可重入函数是线程安全。
但是对于C++ 的成员函数来说,他们可以访问成员变量,所以同一个实例在被不同的线程访问的时候就有可能造成数据共享,也就有可能造成线程不安全。所以可重入的成员函数未必是线程安全的。
无论对于C语言的函数还是C++类的成员函数,线程安全的函数都未必是可重入的。
例子1 可重入却线程不安全的成员函数
class Counter
{
public:
Counter() { n = 0; }
void increment() { ++n; }
void decrement() { --n; }
int value() const { return n; }
private:
int n;
};
4 这两个概念和锁的关系
另外可重入概念只和函数访问的变量类型有关,和是否使用锁没有关系!!
而线程安全和锁的使用关系密切,很多时候线程安全是靠锁来保证的。