线程同步另外几种方式:自旋锁,信号量,关卡
自旋锁:
自旋锁适用的情况:锁被其他线程短期持有(很快会被释放),而且等待该锁的线程不希望在阻塞期间被取消调度,因为这会带来一些开销。
自旋锁与互斥锁的区别:
互斥锁通过休眠的方式来阻塞线程。(线程被暂时取消调度,切换至其他可运行的线程)
自旋锁通过忙等的方式来阻塞线程。(线程不会被取消调度,一直处于运行状态)
应用:
非抢占式内核中非常有用:比如在非抢占式内核中,中断处理程序是不能被休眠的,它们唯一可以使用的同步原语只有自旋锁。
在用户级并不十分有用:在分时调度类中运行的用户级线程可以被取消调度 - 当它的时间片用完或有一个更高有效级的线程到来的时候。
自旋锁初始化:
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
参数:
lock : 自旋锁变量指针
pshared : 自旋锁的只有一个属性,pshared代表线程进程共享同步属性,它指示了自旋锁如何获得。
pshared = PTHREAD_PROCESS_SHARED : 自旋锁可以被访问该锁的底层内存的线程获得,即使这些线程来自于不同的进程
pshared = PTHREAD_PROCESS_PRIVATE : 只能被本进程内初始化的线程获取。
返回值:成功返回0,失败返回错误代码。
自旋锁枷锁
int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);
返回值:成功返回0,失败返回错误代码。
要给自旋锁枷锁,可以调用lock和trylock。pthread_spin_lock()在获得锁之前,线程会一直自旋;pthread_spin_trylock()时线程不自旋,如果自旋锁不能立即获得,返回错误EBUSY。
自旋锁解锁
不论使用lock还是tryklock上锁,都可以调用pthread_spin_unlock()解锁
int pthread_spin_unlock(pthread_spinlock_t *lock);
返回值:成功返回0,失败返回错误代码。
注意:不要调用会在持有自旋锁期间休眠的函数,由于自旋等待可能会浪费大量的CPU资源。
信号量:
是一种特俗的变量,一个信号量有一个相关联的整数值,该整数值大于等于0,它可以被增加或减小,但被其的关键访问被保证是原子操作。
通过调用sem_wait对相应的整数值减1,即通常所说的。若当前的信号量的值等于0,则该操作会阻塞线程。
另外一种通过sem_post对相应值加1,即通常说的。
信号量分类:信号量可作如下分类:
二元灯:灯亮/灯灭两种状态,取值1/0,这是与互斥锁相似的
多元灯:大于1的灯数,表示有多个资源,成为多元灯
信号量即可用于进程间同步也可以用于线程间同步。
信号量使用的头文件semaphore.h
初始化函数
int sem_init(sem_t *sem, int pshared, unsigned int value);
sem : 信号量变量指针
pshared : 信号量类型,为0表示为当前进程的局部信号量,为1表示可以在多个进程间共享
value : sem的初始值
返回值:调用成功返回0,失败返回-1
信号量减1
int sem_wait(sem_t *sem);
功能:以原子操作的方式将信号量的值减1
参数:
sem:sem_init()初始化的信号量
返回值:成功返回0,失败返回-1
信号量加1
int sem_post(sem_t *sem);
功能:以原子操作的方式将信号量的值加1
返回值:成功返回0,失败返回-1
信号量清理函数
int sem_destroy(sem_t *sem);
返回值:成功返回0,失败返回-1
互斥锁用于互斥,信号量用于同步。
这是互斥锁/互斥量和信号量/信号灯的根本区别。
1.互斥量的值只能为0/1,信号量的值可以大于1。
即互斥量只能实现一个资源的互斥访问,但信号量可以实现多个资源的多线程互斥和同步。
当信号量为单值信号量,也可以完成一个资源的互斥访问。
互斥量的加锁和解锁操作必须由同一个线程分别对应使用,而信号量可以由一个线程释放,另一个线程得到。
互斥和同步的概念:
互斥:某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。互斥访问是无序访问,无法限制访问者对资源的访问顺序。
同步:在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源
信号量例子
关卡
关卡是一种同步机制,可以协调多个并行工作的线程,关卡使每一个线程等待直至所有合作的线程都到达了同一点,然后再从一个点开始继续执行。
pthread_join()函数是一个关卡机制,它使一个线程等待另一个线程的结束。比如在主线程中等待所有子线程的结束然后释放资源结束程序。
关卡比pthread_join()更一般化,它允许任意数量的线程等待
关卡使用的头文件 pthread.h
初始化函数
int pthread_barrier_init( pthread_barrier_t *restrict barrier, const pthread_barrierattr_t *restrict attr, unsigned int count);
参数:
barrier: 关卡变量指针
attr: 关卡属性,NULL为使用默认属性
count: 指定在所有线程继续运行之前,必须达到该关卡的线程的个数
返回值:成功返回0,失败返回出错编号
等待函数
int pthread_barrier_wait( pthread_barrier_t *barrier);
返回值:成功返回0或PTHREAD_BARRIER_SERIAL_THREAD,失败返回出错编号
PTHREAD_BARRIER_SERIAL_THREAD: 返回该值的线程为主线程,负责处理所有线程完成工作后的结果
说明:在到达关卡的线程数量没有达到count个数时,调用pthread_barrier_wait()函数的线程都会休眠,直到count个线程的最后一个线程调用pthread_barrier_wait()函数时,所有线程都被唤醒。
资源释放函数
int pthread_barrier_destroy( pthread_barrier_t *barrier);
返回值:成功返回0,失败返回错误编号
初始化关卡对象时,指定的线程数量为工作线程量加1,加1是加上主线