二 Linux--多线程

线程的同步和互斥基本概念概述:现在操作系统基本都是多任务的操作系统,同时有大量可以调度的实体在运行 。在多任务操作系统当中 , 同时运行的多个任务可能:

  • 都需要访问/使用同一种资源
  • 多个任务之间有依赖关系,某个任务的运行依赖于另一个任务
同步和互斥就是用来解决上述两个问题的 。
同步和互斥的概念:
  • 互斥是要求两个任务不能同时占用资源,会相互排序,必须等待一个线程运行完毕 , 另外一个线程才能过来使用资源 。
  • 同步是一种更为复杂的互斥,在互斥的基础上,要求两个任务的执行存在先后顺序 。
其他相关概念:
  • 临界资源: 多线程执行流共享的资源就叫做临界资源
  • 临界区: 每个线程内部,访问临界资源的代码,就叫做临界区
  • 原子性: 不会被任何调度机制打断的操作,该操作只有两态(无中间态,即使被打断,也不会受影响),要么完成,要么未完成
互斥量mutex概念: 多个线程对一个共享变量进行操控时,会引发数据不一致的问题 。此时就引入了互斥量(也叫互斥锁)的概念 , 来保证共享数据操作的完整性 。在被加锁的任一时刻,临界区的代码只能被一个线程访问 。
互斥锁是一种简单的加锁的方法来控制对共享资源的访问 , 互斥锁只有两种状态,即加锁(lock)和解锁(unlock) 。
代码的要求:
  • 代码必须要有互斥行为:当代码进入临界区执行时,不允许其他线程进入该临界区 。
  • 如果多个线程同时要求执行临界区的代码 , 并且临界区没有线程在执行,那么只能允许一个线程进入该临界区 。
  • 如果线程不在临界区中执行,那么该线程不能阻止其他线程进入临界区 。

二 Linux--多线程

文章插图
互斥量的接口互斥量其实就是一把锁,是一个类型为pthread_mutex_t 的变量,使用前需要进行初始化操作,使用完之后需要对锁资源进行释放 。
  • 初始化互斥量
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);功能: 初始化一个互斥锁参数: mutex:互斥锁地址,类型是pthread_mutex_t attr:设置互斥量的属性,通常可采取默认属性,即可将attr改为NULL 可以使用宏pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER静态初始化互斥锁这种方法等价于使用NULL指定的attr参数调用pthread_mutex_init()来完成动态初始化,不同之处在于PTHREAD_MUTEX_INITIALIZER宏不进行错误检查返回值: 成功:0 成功申请的锁默认是打开的 失败:非0 错误码注意:restrict是C语言中的一种类型限定符,用于告诉编译器,对象已经被指针引用,不能通过除该指针外所有其他直接或者间接的方式修改该对象的内容 。
  • 加锁
int pthread_mutex_lock(pthread—mutex—t *mutex);功能: 对互斥锁上锁,若互斥锁已经上锁,则调用者阻塞,直到互斥锁解锁后再上锁 。参数: mutex:互斥锁地址 。返回值: 成功:0 失败:非0错误码int pthread_mutex_trylock(pthread_mutex_t *mutex);调用该函数时,若互斤锁未加锁 , 则上锁,返回0;若互斥锁已加锁,则函数直接返回失败 , 即EBUSY
  • 解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);功能: 对指定的互斥锁解锁参数: mutex:互斥锁地址返回值: 成功:0 失败:非0错误码
  • 销毁互斥量
int pthread_mutex_destroy(pthread_mdtex_t *mutex);功能: 销毁指定的一个互斥锁 。互斥锁在使用完毕后,必须要对互斥锁进行销毁 , 以释放资源参数: mutex:互斥锁地址返回值: 成功:0 失败:非0错误码注意:
  • 使用 PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要销毁
  • 不要销毁一个已经加锁的互斥量
  • 已经销毁的互斥量,要确保后面不会有线程再尝试加锁
  • 加锁的粒度要够小
代码示例:写了一个抢票的小程序 , 用全局变量ticket代表现有票数,五个线程分别执行抢票的操作,也就是对ticket进行减减的操作,直到票数为0就停止抢票

推荐阅读