要写出线程安全的类和函数,有状态、无状态,可重入、不可重入这四个概念绕不开。什么是线程安全的类和函数呢?就是可以被多个线程调用而不会出现数据的错乱的类的对象和函数。导致线程不安全的根本原因是函数或类对象中使用了共享数据(类静态成员变量、全局变量等),由于没有对这些共享数据进行同步操作而导致数据错乱。根据这个原因就把函数或类分成下边两种情况:
1.无状态的
这类函数和类对象不包含任何其他作用域中的变量或者对象,计算过程中的临时状态仅存在于自己的线程栈上的局部变量中,并且只能由当前的线程访问,不会受到其他线程的影响,这些无状态的类对象和函数时线程安全的。那么相对的就属于有状态的。
非线程安全例子:
1: 函数中引用了全局可变变量或者对象。并且函数的运行状态依赖该变量当前状态的影响,此时每次的运行结果是不确定的,受到其他线程的影响,是非线程安全的。
2: 函数中引用或者返回了全局静态变量,局部静态变量,类静态变量,因为静态变量只会初始化一次,他的状态会受到其他线程的影响。
3: 如果一个类中含有可变的静态变量,那么此类也是非线程安全的,当我们在不同的线程中定义了该类的对象时(其实不用定义该类的对象也可),如果在线程中改变了该类的静态成员变量,那么也会造成数据的不一致。
4:调用了线程不安全的函数,那么该函数挥着类也是非线程安全的。
二 可重入函数

第一类:不保护共享变量的函数,
在函数中访问全局变量和堆。共享变量在多线程中是共享数据比如全局变量和堆,如果不保护共享变量,多线程时会出bug。
可以通过同步机制来保护共享数据,比如加锁。
第二类:函数中分配,重新分配释放全局资源。
与上面第一点基本相同,通过加锁可解决
第三类:返回指向静态变量的指针的函数,函数中通过句柄和指针的不直接访问。
比如,我们要计算a,b两个变量的和,于是将a,b的指针传入某一个函数,然而此时可能有另一个线程改变了a,b的值,此时在函数中我们通过地址取到的两个数的值已经改变了,所以计算出的结果也就是错的了。
又比如某些函数(如gethostbyname)将计算结果放在静态结构中,并返回一个指向这个结构的指针。在多线程中一个线程调用的结构可能被另一个线程覆盖。可以通过重写函数和加锁拷贝技术来消除。加锁拷贝技术指在每个位置对互斥锁加锁,调用线程不安全函数,动态的为结果分配存储器,拷贝函数返回的结构,然后解锁。
第四类:调用线程不安全函数
如果我们通过一些同步手段,可以写出线程安全的函数。如果没有同步措施,通常在单线程中如果我们向某个变量写入值然后在读值我们总能得到正确的结果或者说唯一的确定的一个结果,但是当这些操作在不同的线程中执行时情况就会变的非常复杂,我们无法保证正在执行读操作的线程能正确的读到其他线程所做的改变,并且在没有同步机制的环境下,编译器或者处理器会对我们程序的执行顺序进行一些调整,这些调整在单线程环境下不会产生影响,但是在多线程环境中会造成一些意想不到的结果,如果我们的线程之间相互影响,或者我们想让某些线程按照我们的安排来执行,此时如果让他们完全由处理器去控制,就会失去控制,此时就算没有线程之间没有共享数据也会产生错误,因此我们需要一些额外的手段来保证线程的正确执行。
通常我们通过加锁来实现数据的同步和保护,加锁的目的就是保证同一时刻只有一个线程在操作我们所要保户的代码区的共享数据,达到数据的一致性和同步性,但是我们不能盲目的加锁,碰到数据就加锁,锁是需要消耗资源的,如果我们盲目的加锁会造成不必要的资源浪费。因此我们要仔细分析程序中的数据共享性,仅对那些共享的数据代码块加锁即可,但是有时候加锁也并不达到效果,当我们的锁在一个类中时,我们要对类的某个实例进行保护时,通常我们在成员函数中加锁,如果我们不小心把成员变量通过成员函数的返回值或者通过成员函数一个引用或者指针参数,把成员变量传递到了锁的保护范围之外,此时成员变量的正确性已经没有保证了,因此对于锁的使用要很小心。
如果我们想实现线程之间的同步,而不是让处理器来控制线程或者进程的执行,我们可以用信号量pv操作来实现,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目 .信号量对象对线程的同步方式与前面几种方法不同,信号允许多个线程同时使用共享资源,这与操作系统中的PV操作相同。它指出了同时访问共享资源的线程最大数目。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。
下面还有两种也会用到的方式
互斥量:采用互斥对象机制。 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源 安全共享,还能实现不同应用程序的公共资源安全共享 .互斥量比临界区复杂。因为使用互斥不仅仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。
事 件: 通过通知操作的方式来保持线程的同步,也可以方便实现对多个线程的优先级比较的操作 .
总结下来,在编写线程安全函数时,要注意两点: