欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

线程同步互斥机制

程序员文章站 2022-07-14 16:31:10
...

线程同步互斥机制

线程互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

例如 :
线程同步互斥机制
线程同步互斥机制
出现这种情况是因为操作系统在内核中对线程的调用的来回切换。代码可以并发的切换到其他的线程。

互斥锁

互斥锁以排他方式防止共享数据被并发访问。
互斥锁是一个二元变量,只有两种状态 开锁 和 上锁。
将某个共享资源与某个特定互斥锁在逻辑上绑定。

操作函数

在访问共享资源时,首先申请互斥锁,如果该互斥处于开锁状态,则申请到该锁对象,并立即上锁,防止其他线程访问该资源。如果互斥锁处于锁定状态,默认阻塞当前进程。
只有锁定该互斥锁的进程才能释放该互斥锁。

初始化锁

动态分配
在使用互斥锁前,需要定义该互斥锁

pthread_mutex_t lock;

pthread_mutex_init() 初始化函数

int pthread_mutex_init(pthread_mutex_t *restrict mutex,
              const pthread_mutexattr_t *restrict attr);
第一个参数为要初始化的互斥锁。
第二个参数指向属性对象的指针,该属性对象定义要初始化的互斥锁的属性。

静态分配

利用宏 PTHREAD_MUTEX_INITIALIZER 初始化静态分配的互斥锁

销毁互斥锁

pthread_mutex_destroy() 函数

int pthread_mutex_destroy(pthread_mutex_t *mutex);

注意:
静态分配的互斥量不需要销毁。
不要销毁一个已经加锁的互斥量。
已经销毁的互斥量要保证后面不会有线程再尝试加锁。

申请锁资源

pthread_mutex_lock() 函数

int pthread_mutex_lock(pthread_mutex_t *mutex);

函数以阻塞方式申请互斥锁
互斥锁处于未加锁状态,函数会将互斥量锁定,同时返回成功。
如果互斥锁处于加锁状态,或者未能竞争到锁资源,则函数陷入阻塞,等待互斥锁解锁。

非阻塞方式申请互斥锁的函数为

int pthread_mutex_trylock(pthread_mutex_t *mutex);

释放锁资源

pthread_mutex_unlock()

int pthread_mutex_unlock(pthread_mutex_t *mutex);
成功返回0,否则返回指明错误的错误编号。

释放锁资源只能由占有该互斥锁的线程完成。

现在我们将前面的程序进行修改
线程同步互斥机制

线程同步互斥机制

条件变量

前面介绍的互斥锁可以解决资源的互斥访问,但并不完美。
例如:我们有一个全局变量 i,j 线程 A 和 B都要访问,线程A需要互斥的执行i++,j–,线程 B 需要互斥的在i == j 的时候执行 do()操作。
此时如果只是用互斥锁 ,可能导致 do() 永远执行不到。

分析:
1. 线程 A 抢占到互斥锁,执行操作,完成释放互斥锁。
2. 线程 A 和 B 都有可能抢占到锁,如果 B 抢占到,条件不满足,退出。
如果 A 抢占到 ,则执行操作。
整体来看,在整个程序中线程 B 仅仅只有一种情况需要执行 do() 操作。但是 B 有可能会争取到锁,不停的申请和释放互斥锁将造成资源的浪费。

所以此时我们就需要用到条件变量。
我们对上面的代码进行改进:
如果线程 B 抢占到互斥锁,在 i != j 的 情况下,释放互斥锁,使线程等待该条件变量。
如果线程 A 抢占到互斥锁,在 i != j 时继续执行,而在条件变量满足时释放互斥锁。并通知线程 B,从而使 do() 得以执行,提高了访问效率。

条件变量不能单独使用,需要配合互斥锁一起实现对资源的互斥访问。

操作函数

初始化

动态分配:
pthread_cond_init() 函数
在使用条件变量前,需要定义条件变量
pthread_cond_t condtion;

int pthread_cond_init(pthread_cond_t *restrict cond,
              const pthread_condattr_t *restrict attr);
第一个参数为只想要初始化或损坏的条件变量的指针
第二个参数为指向属性对象的指针

静态分配:
同互斥量相同:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

销毁

pthread_cond_destroy()函数

int pthread_cond_destroy(pthread_cond_t *cond);

通知等待的条件变量

pthread_cond_signal() 函数

int pthread_cond_signal(pthread_cond_t *cond);
用来通知等待条件变量的第一个线程
如果 cond 上没有阻塞任何线程,则函数不起作用。

int pthread_cond_broadcast(pthread_cond_t *cond);
用来通知等待条件变量的所有线程

等待条件变量

pthread_cond_wait() 函数

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
第一个参数指向要等待的条件变量
第二个参数指向与条件变量 cond 相关联的互斥锁的指针
如果某线程因等待条件变量进入等待状态时,将隐含释放其申请的互斥锁。
在返回时,隐含申请到该互斥锁的操作。

基于条件变量的生产者消费者模型

线程同步互斥机制
线程同步互斥机制
线程同步互斥机制
线程同步互斥机制
线程同步互斥机制

信号量

此时我们介绍的信号量是 POSIX 信号量,前面进程间通信所用到的信号量是 SYSTEM V 信号量,不同的是 SYSTEM V中的信号量只能用于进程间同步。

初始化

sem_init() 函数

int sem_init(sem_t *sem, int pshared, unsigned int value);
第一个参数和前面的互斥量,条件变量相同
第二个参数 pshared == 0 表示在线程间共享
         pshared != 0 表示在进程间共享
第三个参数 信号量初始值

销毁

sem_destroy()函数

int sem_destroy(sem_t *sem);

等待信号量

sem_wait() 函数

int sem_wait(sem_t *sem);
等待信号量,会将信号量 -1

发布信号量

sem_post()

int sem_post(sem_t *sem);
发布信号量,会将信号量 +1

读写锁

当我们在对数据的读写操作时,很多情况下是大量的读操作,而少量的写操作。
显然,此时使用互斥锁也会极大的影响性能。
此时读写锁机制就可以用来处理这种读多写少的情况

读写锁机制

  1. 如果当前线程读数据,则允许其他线程执行读操作,但不允许写操作。
  2. 如果当前线程写数据,其他线程的读,写操作均不允许。

操作

定义全局变量

pthread_rwlock_t rwlock;

初始化/销毁

pthread_rwlock_init()函数

int pthread_rwlock_init(pthread_rwlock_t rwlock, 
                        const pthread_rwlockattr_t* restrict attr);
第一个参数指向要初始化的读写锁指针
第二个参数指向属性对象的指针

静态初始化

PHTREAD_RWLOCK_INITIALIZER 宏 进行静态初始化

pthread_rwlock_destroy() 函数销毁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

函数成功返回 0 ,否则返回错误编号。

申请读锁

pthread_rwlock_rdlock() 函数

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
函数以阻塞方式来申请读锁

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
函数以非阻塞方式来申请读锁 

申请写锁

ptherad_rwlock_wrlock()函数

int pthread_rwlock_wrlock(pthread_lock_t *rwlock);
函数以阻塞方式来申请写锁
int phtread_rwlock_trywrlock(pthread_lock_t *rwlock);
函数以非阻塞方式来申请写锁

解锁

pthread_rwlock_unlock()

ptherad_rwlock_unlock(pthread_rwlock_t *rwlock);
如果用来释放读锁,但当前还有其他读锁定时,则保持读锁定状态。
如果释放的是最后一个读锁或写锁,则读写锁将处于解锁状态。
成功返回 0 ,否则返回错误编号并指明错误

我们先验证读锁的并发读
线程同步互斥机制
线程同步互斥机制
线程同步互斥机制

线程同步互斥机制

我们来验证读写锁同时存在的情况。
线程同步互斥机制
线程同步互斥机制
线程同步互斥机制

相关标签: linux