线程间同步与互斥
-
由于线程共享进程的资源和地址空间,因此在对这些资源进行操作时,必须考虑到线程间资源访问的同步与互斥问题。
-
互斥锁
- 是用一种简单的加锁方法来控制对共享资源的
原子操作。这个互斥锁只有两种状态,也就是上锁(lock)和解锁(unlock),可以把互斥锁看作某种意义上的全局变量。 - 同一时刻只能有一个线程掌握某个互斥锁,拥有上锁状态的线程能够对共享资源进行操作(
即某线程上锁即表示对共享的资源拥有操作权限)。 - 若其他线程希望上锁一个已经被上锁的互斥锁,则该线程就会挂起,直到上锁的线程解锁为止。即互斥锁保证让每个线程对共享资源
按顺序进行原子操作。
- 是用一种简单的加锁方法来控制对共享资源的
graph LR
A(互斥锁主要API函数)-- 互斥锁初始化--->B0(pthread_mutex_init)
A--互斥锁上锁--->B1(pthread_mutex_lock)
A--互斥锁判断上锁--->B2(pthread_mutex_trylock)
A--互斥锁解锁--->B3(pthread_mutex_unlock)
A--互斥锁销毁--->B4(pthread_mutex_destroy)
C(互斥锁类型)--阻塞式-->D0(快速互斥锁)---调用线程会阻塞直至线程解锁为止
C--阻塞式-->D1(递归互斥锁)---能够成功地返回,并且增加调用线程互斥加锁的次数
C--非阻塞式-->D2(检错互斥锁)---为快速互斥锁的非阻塞版本,它会立即带一个错误信息返回
- 信号量
- 其概念在前文Linux学习笔记12 - 进程间通信(IPC)(三)已有介绍。
- 其本质上是一个
非负的整数计数器(sem_val=>0),它被用来控制对公共资源的访问,广泛用于进程或线程间的同步与互斥。 - PV 原子操作的工作原理:
- 一次 P 操作使
sem_val--,而一次 V 操作使sem_val++。 - 当信号量
sem_val>=0,该进程(或线程)具有公共资源的访问权限;相反,当信号量sem < 0,该进程(或线程)就将阻塞直到信号量sem >=0为止。
- 一次 P 操作使
- 互斥与同步的使用
- 用于互斥时,几个进程(或线程)往往只设置一个信号量;用于同步时,往往会设置多个信号量,并安排不同的初始值来实现它们之间的顺序执行。
graph LR
A(无名信号量主要API函数)-- 初始化--->B0(pthread_mutex_init)
A--P操作,阻塞式--->B1(sem_wait)
A--P操作,非阻塞式--->B2(sem_trywait)
A--V操作--->B3(sem_post)---信号量的值加一同时唤醒等待的进程
A--获取信号量值--->B4(sem_getvalue)
A--删除信号量--->B5(sem_destroy)
线程属性
pthread_create()函数的第二个参数(pthread_attr_t *attr)表示线程的属性,缺省属性值是NULL。- 绑定属性
- 在“一对一”的线程机制,也就是一个用户线程对应一个内核线程,即指一个用户线程固定地分配给一个内核线程,因为 CPU 时间片的调度是面向内核线程。因此具有绑定属性的线程可以保证在需要的时候总有一个内核线程与之对应。而与之对应的非绑定属性就是指用户线程和内核线程的关系不是始终固定的,而是由系统来控制分配的。
- 分离属性
- 是用来决定一个线程以什么样的方式来终止自己。在非分离情况下,当一个线程结束时,它所占用的系统资源并没有被释放,也就是没有真正的终止。只有当
pthread_join()函数返回时,创建的线程才能释放自己占用的系统资源。 - 在此属性情况下,一个线程结束时立即释放它所占有的系统资源。但也要注意的是,如果设置一个线程的此属性,而这个线程运行又非常快,那么它很可能在
pthread_create()函数返回之前就终止了,其终止以后就可能将线程号和系统资源移交给其他的线程使用,这时调用pthread_create()的线程就得到了错误的线程号。
- 是用来决定一个线程以什么样的方式来终止自己。在非分离情况下,当一个线程结束时,它所占用的系统资源并没有被释放,也就是没有真正的终止。只有当
- 绑定属性
graph LR
A(属性设置主要函数)-- 初始化--->B0(pthread_attr_init)
A--绑定属性--->B1(pthread_attr_setscope)
A--分离属性--->B2(pthread_attr_setdetachstate)
A--获取线程优先级--->B3(pthread_attr_getschedparam)
A--设置线程优先级--->B4(pthread_attr_setschedparam)
A--分配的属性结构指针进行销毁回收--->B5(pthread_attr_destroy)
- 参考文献:《Linux嵌入式应用程序开发标准教程》