当前位置: 主页 > 程序 >

线程同步-锁

时间:2021-10-04  作者:haden   点击:
【摘要】线程之间的锁有:互斥锁、自旋锁、读写锁。 1 互斥锁 互斥锁用于控制多个线程对他们之间共享资源互斥访问的一个信号量。也就是说是为了避免多个线程在某一时刻同时操作一个共享

线程之间的锁有:互斥锁、自旋锁、读写锁。

1 互斥锁

互斥锁用于控制多个线程对他们之间共享资源互斥访问的一个信号量。也就是说是为了避免多个线程在某一时刻同时操作一个共享资源。例如线程池中的有多个空闲线程和一个任务队列。任何是一个线程都要使用互斥锁互斥访问任务队列,以避免多个线程同时访问任务队列以发生错乱。

在某一时刻,只有一个线程可以获取互斥锁,在释放互斥锁之前其他线程都不能获取该互斥锁。如果其他线程想要获取这个互斥锁,那么这个线程只能以阻塞方式进行等待。

头文件:#include < pthread.h >
类型:pthread_mutex_t

// 动态方式创建锁,相当于new动态创建一个对象
pthread_mutex_init(pthread_mutex_t * mutex, const phtread_mutexattr_t * mutexattr);

// 以静态方式创建锁,在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量。
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

//释放互斥锁,相当于delete
pthread_mutex_destory(pthread_mutex_t *mutex);

//以阻塞方式运行的。如果之前mutex被加锁了,那么程序会阻塞在这里。
pthread_mutex_lock(pthread_mutex_t *mutex);

pthread_mutex_unlock(pthread_mutex_t *mutex);

//会尝试对mutex加锁。如果mutex之前已经被锁定,返回非0;如果mutex没有被锁定,则函数返回并锁定mutex
//该函数是以非阻塞方式运行了。也就是说如果mutex之前已经被锁定,函数会返回非0,程序继续往下执行。
int pthread_mutex_trylock(pthread_mutex_t * mutex);

2 自旋锁

首先我们说明互斥锁的工作原理,互斥锁是是一种sleep-waiting的锁。假设线程T1获取互斥锁并且正在core1上运行时,此时线程T2也想要获取互斥锁(pthread_mutex_lock),但是由于T1正在使用互斥锁使得T2被阻塞。当T2处于阻塞状态时,T2被放入到等待队列中去,处理器core2会去处理其他任务而不必一直等待(忙等)。也就是说处理器不会因为线程阻塞而空闲着,它去处理其他事务去了。

而自旋锁就不同了,自旋锁是一种busy-waiting的锁。也就是说,如果T1正在使用自旋锁,而T2也去申请这个自旋锁,此时T2肯定得不到这个自旋锁。与互斥锁相反的是,此时运行T2的处理器core2会一直不断地循环检查锁是否可用(自旋锁请求),直到获取到这个自旋锁为止。

从“自旋锁”的名字也可以看出来,如果一个线程想要获取一个被使用的自旋锁,那么它会一致占用CPU请求这个自旋锁使得CPU不能去做其他的事情,直到获取这个锁为止,这就是“自旋”的含义。

当发生阻塞时,互斥锁可以让CPU去处理其他的任务;而自旋锁让CPU一直不断循环请求获取这个锁。通过两个含义的对比可以我们知道“自旋锁”是比较耗费CPU的。

互斥锁有一个缺点,他的执行流程是这样的 托管代码 - 用户态代码 - 内核态代码、上下文切换开销与损耗,假如获取到资源锁的线程A立马处理完逻辑释放掉资源锁,如果是采取互斥的方式,那么线程B从没有获取锁到获取锁这个过程中,就要用户态和内核态调度、上下文切换的开销和损耗。所以就有了自旋锁的模式,让线程B就在用户态循环等着,减少消耗。

自旋锁比较适用于锁使用者保持锁时间比较短的情况,这种情况下自旋锁的效率要远高于互斥锁。

头文件:< linuxspinlock.h >
自旋锁的类型:spinlock_t

// 初始化
spin_lock_init(spinlock_t *x);

// 只有在获得锁的情况下才返回,否则一直“自旋”
spin_lock(x);

// 如立即获得锁则返回真,否则立即返回假
spin_trylock(x);

//释放锁
spin_unlock(x);

// 该宏用于判断自旋锁x是否已经被某执行单元保持(即被锁),如果是,返回真,否则返回假。
spin_is_locked(x)

3 读写锁

计算机中某些数据被多个进程共享,对数据库的操作有两种:一种是读操作,就是从数据库中读取数据不会修改数据库中内容;另一种就是写操作,写操作会修改数据库中存放的数据。因此可以得到我们允许在数据库上同时执行多个“读”操作,但是某一时刻只能在数据库上有一个“写”操作来更新数据。这就是一个简单的读者-写者模型。

头文件:#include < pthread.h >
类型:pthread_rwlock_t

//动态方式创建读写锁
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);

//以静态方式创建读写锁
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

//读的方式加锁和尝试(没锁上就立即返回)加锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

//用写的方式加锁和尝试(没锁上就立即返回)加锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

//解锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

//读写锁销毁
int pthread_rwlock_distroy(pthread_rwlock_t *rwlock);
顶一下
(0)
0%
踩一下
(0)
0%
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
验证码: 点击我更换图片

推荐内容