线程安全 (可重入函数)
线程之间共享全局数据段、静态数据,引起非线程安全。线程安全可以通过线程同步对临界资源访问进行控制来实现。
有些系统调用或者库函数的实现时发生不安全现象,在多线程环境下就需要使用这些函数的安全版本,即可重入函数。
例如:字符串分割函数
普通版本:char* strtok(char* sourstr,const char* flag)
可重入版本:char* strtok_r(char* sourstr,const char* flag,char ** res)
printf()不是线程安全函数
线程同步:
四种方式:信号量、互斥锁、读写锁、条件变量。用户可以通过这四个方式对线程进行同步操作。下面分别介绍一下
信号量:在进程间通信时我们已经了解过信号量了,这里与进程间的信号量作用相似,当线程访问一些有限的共享资源的时候,就必须做到线程同步访问。
信号量的使用方式:#include<semaphore.h>
初始化:int sem_init(sem_t *sem,int shared,int val);
信号量sem一般被定义在线程共享的全局数据段,sem_init函数将信号量sem 的初始值设置为val,shared参数控制这个信号量是否可以在多个进程之间共享,但是Linux对此不支持。
P操作:对信号量sem进行-1操作,如果结果小于0,此函数会阻塞,直到其他线程执行V操作。
int sem_wait(sem_t *sem);
V操作:对信号量sem进行+1操作
int sem_post(sem_t *sem);
销毁:销毁信号量
int sem_destroy(sem_t *sem);
互斥锁:只能在线程之间使用的一种控制临界资源访问的机制,如果一个线程想要访问临界资源,必须先加锁,用完之后再解锁。当一个线程访问临界资源时,其他线程的访问就会阻塞,不能访问,直到该线程使用完解锁后。互斥锁一般放在线程共享的全局数据段。
互斥锁的使用方式:#include<pthread.h>
初始化:int pthread_mutex_init(pthread_mutex_t* mutex,pthread_mutexattr_t *attr);
加锁:int pthread_mutex_lock(pthread_mutex_t* mutex);
解锁:int pthread_mutex_unlock(pthread_mutex_t* mutex);
销毁锁:int pthread_mutex_destroy(pthread_mutex_t* mutex);
条件变量:用于线程之间同步共享数据的值,条件变量提供了一种线程间的通信机制:当共享数据达到某个值时,唤醒这个共享数据的线程。
条件变量的使用方式:#include<pthread.h>
初始化条件变量:int pthread_cond_init(pthread_cond_t* cond,pthread_condattr_t *cond_attr);
销毁条件变量:int pthread_cond_destroy(pthread_cond_t* cond);
以广播的方式唤醒所有等待条件变量的线程:int pthread_cond_broadcast(pthread_cond_t* cond);
唤醒一个等待目标变量的线程:int pthread_cond_signal(pthread_cond_t* cond);
等待目标条件变量:int pthread_cond_wait(pthread_cond_t* cond,pthread_mutex_t* mutex);