一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误。 不需要共享时,请为每个线程提供一个专用的数据副本。如果共享非常重要,则提供显式同步,以确保程序以确定的方式操作。通过将过程包含在语句中来锁定和解除锁定互斥,可以使不安全过程变成线程安全过程,而且可以进行串行化。 很多函数并不是线程安全的,因为他们返回的数据是存放在静态的内存缓冲区中的。通过修改接口,由调用者自行提供缓冲区就可以使这些函数变为线程安全的。 操作系统实现支持线程安全函数的时候,会对POSIX.1中的一些非线程安全的函数提供一些可替换的线程安全版本。 例如,gethostbyname()是线程不安全的,在Linux中提供了gethostbyname_r()的线程安全实现。 函数名字后面加上"_r",以表明这个版本是可重入的(对于线程可重入,也就是说是线程安全的,但并不是说对于信号处理函数也是可重入的,或者是异步信号安全的)。 多线程程序中常见的疏忽性问题 1> 将指针作为新线程的参数传递给调用方栈。 2> 在没有同步机制保护的情况下访问全局内存的共享可更改状态。 3> 两个线程尝试轮流获取对同一对全局资源的权限时导致死锁。其中一个线程控制第一种资源,另一个线程控制第二种资源。其中一个线程放弃之前,任何一个线程都无法继续 操作。 4> 尝试重新获取已持有的锁(递归死锁)。 5> 在同步保护中创建隐藏的间隔。如果受保护的代码段包含的函数释放了同步机制,而又在返回调用方之前重新获取了该同步机制,则将在保护中出现此间隔。结果具有误导性。对于调用方,表面上看全局数据已受到保护,而实际上未受到保护。 6> 将UNIX 信号与线程混合时,使用sigwait(2) 模型来处理异步信号。 7> 调用setjmp(3C) 和longjmp(3C),然后长时间跳跃,而不释放互斥锁。 8> 从对*_cond_wait() 或*_cond_timedwait() 的调用中返回后无法重新评估条件。 总结 判断一个函数是不是可重入函数,在于判断其能否可以被打断,打断后恢复运行能够得到正确的结果。(打断执行的指令序列并不改变函数的数据) 判断一个函数是不是线程安全的,在于判断其能否在多个线程同时执行其指令序列的时候,保证每个线程都能够得到正确的结果。 如果一个函数对多个线程来说是可重入的,则说这个函数是线程安全的,但这并不能说明对信号处理程序来说该函数也是可重入的。 如果函数对异步信号处理程序的重入是安全的,那么就可以说函数是"异步-信号安全"的。 jacksting 回复于:2007-05-30 11:32:04 个人在Linux上(RH9及其以后的内核)的一点经验。 三个概念,线程安全,可重入函数,信号安全函数。 线程安全,主要是针对数据竞争来说的,就是说:如果数据不需要共享,那就让每个线程私有;如果需要共享,那就加锁。 可重入:就是无论以什么方式多次调用都不会出现问题,不会出现对可能有修改的静态数据的访问,不会出现对全局变量的访问。如果一个函数是可重入的,那一定是线程安全的,反之则不一定。 信号安全,其实也就是异步信号安全,是说线程在信号处理函数当中,不管以任何方式调用你的这个函数如果不死锁不修改数据,那就是信号安全的。也就是说一个可重入函数在信号处理函数当中不影响调用他的人本身的状态,其实就是一个task_struct有很多指针指向它,你通过多线程调一个可重入函数,函数有自己所在的task的代码段和数据段,如果你在信号处理里面调它,实际上是换了一个指向同一个task_struct里面内容的指针来操作,会有问题。这种情形通过加锁是解决不了问题的,比如你正在调用printf过程中,遇到一个信号,转而去处理这个信号,并且在处理这个信号的信号处理函数当中正巧要调用printf,那么屏幕上就是乱的,加锁行不行呢?那就很好玩了。所以printf不是异步信号安全的。 不知道绕来绕去说清楚没有。 总结:信号安全要求最高,可重入次之,线程安全更次之。 Linux实际编程经验,对于多线程程序,调用线程安全就可以了,能重入最好,但是不强求,多线程调用malloc是可以的。但是,在安装信号处理程序的时候,看看你自己调用的是不是异步信号安全函数,怎么看?很简单,所有异步信号安全的函数在他的man中会十分明确的指出来,没有指出的一律就是不安全的。 http://www./jh/23/942090.html |
|