多线程编程-线程同步方式介绍(二)
来源:互联网 发布:curl windows 64 下载 编辑:程序博客网 时间:2024/06/05 16:08
序言
上一篇文章中介绍了死锁和线程同步的几种方式:互斥锁,条件变量,读写锁。
本文将介绍线程同步的另外几种方式:自旋锁,信号量,Barriers。
1. 自旋锁(Spin Locks)
自旋锁适用的情况:锁被其他线程短期持有(很快会被释放),而且等待该锁的线程不希望在阻塞期间被取消调度,因为这会带来一些开销。
自旋锁与互斥锁的区别:
互斥锁通过休眠的方式来阻塞线程。(线程被暂时取消调度,切换至其他可运行的线程)
自旋锁通过忙等的方式来阻塞线程。(busy-waiting, spinning)(线程不会被取消调度,一直处于运行状态)
应用:
[1] 非抢占式内核中非常有用:比如在非抢占式内核中,中断处理程序是不能被休眠的,它们唯一可以使用的同步原语只有自旋锁。
[2] 在用户级并不十分有用:在分时调度类中运行的用户级线程可以被取消调度 - 当它的时间片用完或有一个更高有效级的线程到来的时候。
自旋锁的接口:
[1] 自旋锁初始化
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);参数: lock : 自旋锁变量指针 pshared : 自旋锁的只有一个属性,pshared代表线程进程共享同步属性,它指示了自旋锁如何获得。 pshared = PTHREAD_PROCESS_SHARED : 自旋锁可以被访问该锁的底层内存的线程获得,即使这些线程来自于不同的进程 pshared = PTHREAD_PROCESS_PRIVATE : 只能被本进程内初始化的线程获取。返回值:成功返回0,失败返回错误代码。
[2] 自旋锁枷锁
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。
- [3] 自旋锁解锁
不论使用lock还是tryklock上锁,都可以调用pthread_spin_unlock()解锁int pthread_spin_unlock(pthread_spinlock_t *lock);返回值:成功返回0,失败返回错误代码。注意:不要调用会在持有自旋锁期间休眠的函数,由于自旋等待可能会浪费大量的CPU资源。
2. 信号量(Semaphore)
信号量:信号量是一种特殊的变量,一个信号量有一个相关联的整数值,该整数值大于等于0,它可以被增加或减小,但对其的关键访问被保证是原子操作。
通过调用sem_wait对相应的整数值减1,即通常所说的P操作。若当前的信号量的值等于0,则该操作会阻塞线程
另外一种通过sem_post对相应值加1,即通常说的V操作。
信号量分类:信号量(灯)可作如下分类
二元灯:灯亮/灯灭两种状态,取值1/0。此时与互斥锁相似。
多元灯:大于1的灯数,表示有多个资源,成为多元灯。
信号量适用的情况:信号量既可用于进程间同步,也可以用于线程间同步,这里介绍线程间信号量同步。
信号量函数接口:头文件 semaphore.h
[1] 初始化函数
int sem_init(sem_t *sem, int pshared, unsigned int value); 参数: sem : 信号量变量指针 pshared : 信号量类型,为0表示为当前进程的局部信号量,为1表示可以在多个进程间共享 value : sem的初始值返回值:调用成功返回0,失败返回-1
[2] 信号量值减一
int sem_wait(sem_t *sem); 功能:以原子操作的方式将信号量的值减1参数: sem:sem_init()初始化的信号量返回值:成功返回0,失败返回-1
[3] 信号量值加一
int sem_post(sem_t *sem); 功能:以原子操作的方式将信号量的值加1返回值:成功返回0,失败返回-1
[4] 信号量清理函数
int sem_destroy(sem_t *sem);返回值:成功返回0,失败返回-1
信号量和互斥锁的区别:总的来说,信号量的功能覆盖了互斥锁的功能
[1] 互斥锁用于互斥,信号量用于同步。
- 这是互斥锁/互斥量和信号量/信号灯的根本区别。
[2] 互斥量的值只能为0/1,信号量的值可以大于1。
- 即互斥量只能实现一个资源的互斥访问,但信号量可以实现多个资源的多线程互斥和同步。
- 当信号量为单值信号量,也可以完成一个资源的互斥访问。
[3] 互斥量的加锁和解锁操作必须由同一个线程分别对应使用,而信号量可以由一个线程释放,另一个线程得到。
互斥和同步的概念:
互斥:某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。互斥访问是无序访问,无法限制访问者对资源的访问顺序。
同步:在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源
3. 关卡(Barriers)
Barriers:是一种同步机制,可以协调多个并行工作的线程。Barriers使每一个线程等待直到所有合作的线程都到达了同一点,然后再从这一点开始继续执行。
pthread_join()函数就是一种Barriers机制,它使一个线程等待另一个线程的结束。比如在主线程中等待所有子线程的结束然后释放资源结束程序。
Barriers比pthread_join()更一般化,它允许任意数量的线程等待。
Barriers函数接口:头文件 pthread.h
[1] 关卡初始化函数
int pthread_barrier_init( pthread_barrier_t *restrict barrier, const pthread_barrierattr_t *restrict attr, unsigned int count);参数: barrier: 关卡变量指针 attr: 关卡属性,NULL为使用默认属性 count: 指定在所有线程继续运行之前,必须达到该关卡的线程的个数返回值:成功返回0,失败返回出错编号
[2] 等待函数:当一个线程率先完成工作到达关卡,该线程可调用等待函数来等待其他所有线程赶上它
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()函数时,所有线程都被唤醒。
[3] 资源释放函数
int pthread_barrier_destroy( pthread_barrier_t *barrier);返回值:成功返回0,失败返回错误编号
Barriers使用的注意点
初始化barrier对象时,指定的线程count为工作线程个数+1,加1是加上主线程
在程序中线程并不检测调用的pthread_barrier_wait()的返回值是0还是PTHREAD_BARRIER_SERIAL_THREAD,这是因为我们指定使用主线程来合并其他线程的执行结果
Acknowledgements:
http://www.cnblogs.com/nufangrensheng/p/3521654.html
http://blog.csdn.net/ljianhui/article/details/10813469/
http://blog.csdn.net/bbaiggey/article/details/52943267
2017.08.30
- 多线程编程-线程同步方式介绍(二)
- C语言多线程编程-死锁和线程同步方式介绍(一)
- 多线程编程:线程同步
- 【多线程】多线程编程:线程同步
- 多线程编程中 线程同步的三种方式
- Linux多线程编程(二)线程同步之条件变量
- 多线程之二 线程同步
- 多线程之二:线程同步
- 多线程(二)--线程同步
- 多线程编程与线程同步
- 多线程编程5 线程同步
- 多线程编程 线程互斥 线程同步
- 多线程-线程同步有几种实现方式
- iOS多线程安全锁介绍---线程同步
- Python线程编程(二)线程同步
- Python多线程学习(二、线程的同步)
- Python多线程学习(二、线程的同步)
- 多线程教程之二---线程间同步
- 1036. 跟奥巴马一起编程(15)
- DStream 生成 RDD 实例详解
- linux命令-sed命令使用(2)
- Spark学习笔记(1)Spark初体验
- [Machine Learning][Linear Regression]Feature Scaling
- 多线程编程-线程同步方式介绍(二)
- 如何代表与JAXB空元素空值?(jaxb中引用@XmlNullPolicy仍然不能显示空节点)
- Hadoop学习之HDFS读流程
- MyEclipse中的常用快捷键
- u-boot移植--4、对于NAND FLASH的支持
- ldap学习笔记 1
- python使用pymongo访问MongoDB的基本操作,以及CSV文件导出
- 从论文中寻找知识、思路、最新研究成果
- 1037. 在霍格沃茨找零钱(20)