操作系统02——线程同步问题

一、基本概念

基本任务

协调合作进程的执行次序,使并发执行的各进程间能按照一定规则(或时序)有效的共享资源,以及相互协作,从而使程序的执行具有可再现性

制约关系

  • 间接相互制约关系
    当一个进程使用某临界资源时,另一个想使用该临界资源的进程必须等待。由于共享同一资源形成的关系,也成为互斥

    临界资源

    是一次仅允许一个进程使用的共享资源。
    各进程采取互斥的方式,实现共享的资源称作临界资源。
    属于临界资源的硬件有,打印机,磁带机等;
    软件有消息队列,变量,数组,缓冲区等。诸进程间采取互斥方式,实现对这种资源的共享。

  • 直接相互制约关系
    多个相关的进程在执行次序上的协调。这些进程需要互相等待或者互通消息。例如,进程B需要进程A的结果才能运行,进程B的执行影响进程A是否等待。

二、互斥锁

互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁有两种状态,即加锁( lock )和解锁( unlock )

操作流程:

  1. 在访问共享资源后临界区前,对互斥锁进行加锁;
  2. 在访问完成后释放互斥锁导上的锁。在访问完成后释放互斥锁导上的锁;
  3. 对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。

相关变量与函数

函数/变量 意义
pthread_mutex_t 互斥变量使用特定的数据类型
int pthread_mutex_lock(pthread_mutex_t *mutex) 加锁
int pthread_mutex_unlock(pthread_mutex_t *mutex) 解锁

加锁
int pthread_mutex_lock(pthread_mutex_t *mutex)
在成功完成之后会返回零。其他任何返回值都表示出现了错误。

解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex)
pthread_mutex_unlock是可以解除锁定 mutex 所指向的互斥锁的函数。pthread_mutex_unlock()可释放mutex引用的互斥锁对象。pthread_mutex_unlock()在成功完成之后会返回零。其他任何返回值都表示出现了错误。

#include <pthread.h>
pthread_mutex_t mutex;
int ret;
ret = pthread_mutex_unlock(&mutex); 

三、条件变量

条件变量是利用线程间共享的全局变量进行同步的一种机制

主要包括两个大动作

  • 一个线程等待"条件变量的条件成立"而挂起;
  • 另一个线程使"条件成立"(给出条件成立信号)

为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。
互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起配合使用。

使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其他的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步。

相关变量与函数

函数/变量 意义
pthread_cond_t 条件变量类型
pthread_cond_t cond = PTHREAD_COND_INITIALIZER 静态初始化
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr) 动态初始化
int pthread_cond_destroy(pthread_cond_t *cond) 注销条件变量(只有在没有线程在该条件变量上等待的时候,才能注销这个条件变量,否则返回EBUSY)
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex) 阻塞条件变量,成功返0
int pthread_cond_signal(pthread_cond_t *cond) 使条件变量脱离阻塞状态,继续执行。如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回

四、信号量

信号量有两种实现:传统的System V信号量和新的POSIX信号量。

它们所提供的函数很容易被区分:对于所有System V信号量函数,在它们的名字里面没有下划线。例如,应该是semget()而不是sem_get()。然而,所有的POSIX信号量函数都有一个下划线。

  • POSIX信号量来源于POSIX技术规范的实时扩展方案(POSIX Realtime Extension),常用于线程;头文件为semaphore.h,函数调用为sem_init(),sem_wait(),sem_post(),sem_destory()等
  • system v信号量,常用于进程的同步。System V信号量的头文件为sys/sem.h,函数调用为semctl(),semget(),semop()等

函数对比

System V POSIX 作用
semctl() sem_getvalue()
semget() int sem_post(sem_t *sem) 以原子操作的方式将信号量的值加1
semop() sem_timedwait()
sem_trywait()
int sem_wait(sem_t *sem) 以原子操作的方式将信号量的值减1
int sem_destroy(sem_t *sem) 将用完的信号量清除
int sem_init(sem_t *sem,int pshared,unsigned int value) 创建信号量
sem_close()
sem_open()
sem_unlink()
sem_init(sem_t *sem,int pshared,unsigned int value)

pshared控制信号量的类型,如果其值为0,就表示这个信号量是当前进程的局部信号量,否则信号量就可以在多个进程之间共享,value为sem的初始值。调用成功时返回0,失败返回-1

原子操作:如果两个线程企图同时给一个信号量加1或减1,它们之间不会互相干扰
上一篇:第四章学习笔记


下一篇:结合源码的Linux线程基础详细整理