多线程(17) pthread_sigmask 及 线程安全和可重入
1. pthread_sigmask 用作在主调线程里控制信号掩码
1.1 定义
用来定义线程的信号掩码
1.2 接口(与 sigprocmask 一样)
#include <pthread.h>
#include <signal.h>
int pthread_sigmask (int how, const sigset_t *newmask, sigset_t *oldmask);
1.2.1 参数:
- How:
SIG_BLOCK: 结果集是当前集合参数集的并集
SIG_UNBLOCK: 结果集是当前集合参数集的差集
SIG_SETMASK: 结果集是由参数集指向的集
1.3 pthread_sigmask sigprocmask 区别:
sigprocmask 只能用于单进程单线程,fork的子进程拥有一份屏蔽信号拷贝;
pthread_sigmask 用于多线程 ; 新线程拥有一份pthread_create那个线程的屏蔽信号拷贝;
对于线程信号的处理 , 最好还是用一个线程来统一处理比较, 否则太乱啦!
参考
https://blog.csdn.net/dashoumeixi/article/details/95118392
2. 线程安全 和 可重入
2.1 线程安全 定义-多线程调用ok,如malloc
线程安全的概念比较直观。一般说来,一个函数被称为线程安全的,当且仅当被多个并发线程反复调用时,它会一直产生正确的结果。
2.1.1 例子说明
malloc 是线程安全的,他将操作一个全局链表 , 一旦在执行过程中被sigal中断,直接完蛋
pthread_mutex_lock 他是线程安全吧, 一旦执行过程中被中断, 中断处理中又一次pthread_mutex_lock可能直接死锁或者破坏了内部状态
2.2 可重入 定义-接口内嵌入调用ok,如malloc不是,里面有锁会导致死锁;eventfd_write是的;异步回调中一定要用可重入接口!!!
所谓“重入”,常见的情况是,程序执行到某个函数foo()时,收到信号,于是暂停目前正在执行的函数,转到信号处理 函数,
而这个信号处理函数的执行过程中,又恰恰也会进入到刚刚执行的函数foo(),这样便发生了所谓的重入。
此时如果foo()能够正确的运行,而且处 理完成后,之前暂停的foo()也能够正确运行,则说明它是可重入的。
2.2.1 确保可重入条件
要确保函数可重入,需满足以下几个条件:
1、不在函数内部使用静态或全局数据
2、不返回静态或全局数据,所有数据都由函数的调用者提供。
3、使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。
4、不调用不可重入函数。
2.2.2 不可重入的后果:
不可重入的后果主要体现在象信号处理函数这样需要重入的情况中。
如果信号处理函数中使用了不可重入的函数,则可能导致程序的错误甚至崩溃。
2.2.3 不可重入特点
如果一个函数符合以下条件之一的,则是不可重入的:
(1)调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的。
(2)调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构。
(3)可重入体内使用了静态的数据结构。
2.3 可重入与线程安全 关系
- 线程安全不一定是可重入的,而可重入函数一定是线程安全的。
- 线程安全是多个线程下引起的,但可重入函数可以在只有一个线程的情况下发生。
- 若一个函数中存在全局变量,那么这个函数既不是线程安全的也不是可重入的。
- 线程安全函数能够使不同的线程访问同一块地址空间,而可重入函数要求不同的执行流对数据的操作互不影响结果是相同的。
比如:
strtok函数是既不可重入的,也不是线程安全的;加锁的strtok不是可重入的,但线程安全;
而strtok_r既是可重入的,也是线程安全的。