Linux基础内容(29)—— 额外锁

Linux基础内容(28)—— POSIX信号量与循环队列_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/131844590?spm=1001.2014.3001.5501

目录

1.其他常见的各种锁

自旋锁

库语言的实现

2.读者写者问题

1.读者写者线程

2.读写锁

操作

优先级

伪代码实现


1.其他常见的各种锁

悲观锁:在每次取数据时,总是担心数据会被其他线程修改,所以会在取数据前先加锁(读锁,写锁,行锁等),当其他线程想要访问数据时,被阻塞挂起。
乐观锁:每次取数据时候,总是乐观的认为数据不会被其他线程修改,因此不上锁。但是在更新数据前,会判断其他数据在更新前有没有对数据进行修改。主要采用两种方式:版本号机制和CAS操作。
CAS操作:当需要更新数据时,判断当前内存值和之前取得的值是否相等。如果相等则用新值更新。若不等则失败,失败则重试,一般是一个自旋的过程,即不断重试。

自旋锁

1.我们之前说的锁,被拿取后,其他的线程接收不到锁需要被挂起。而自旋锁是不断地判断是否有锁。

2.无论是普通锁还是自旋锁,都需要考虑场景,目前最大的区分其实是时间效率的事情。所谓的自选就是在不断的循环判断,就意味着线程不挂起,空间被占用,并且不断检查则说明此时多次检查也耗费时间

3.线程如果执行的任务很少,并且达到我们预计需要的时间效益则选择自旋锁。当然判断的标准就是看这两者在实际的项目中run的时间。

库语言的实现

Linux基础内容(29)—— 额外锁_第1张图片

2.读者写者问题

1.读者写者线程

1.在计算机中,其实大部分都在进行复制拷贝的任务。那么其实对于线程也不例外,有两种线程分担了读写的任务。而这两个角色对应的就是读者和写者

2.读者与写者的关系:读者读取时,写者不可以进行操作,避免读取数据被修改;写者写入时,读者也不能干预,避免数据的重复等错误的发生。读者需要写者写入才能读取;写者得有读者读取后释放的空间进行写入。那么由此可知读者和写者的关系为同步互斥

3.写者和写者之间的关系:一次只可以有一个线程对公共资源进行写入,避免出现数据异常的问题。那么也就是说写者之间的关系为 互斥的。

4.读者和读者之间的关系:一个读者读取任务时,另一个读者似乎不会受到什么干扰,那么也就是说二者其实怎么运行是不会有影响的,读者之间的关系是无关系

5.区别于生产消费者模型下的消费者之间的关系存在很大的差异,消费者之间是互斥的,而读者之间是无关系的。这本质其实是因为消费者在处理公共资源后,会讲公共资源的数据拿走,进行运行处理;而读者不同,读者只是拷贝了一份,没有对数据进行改动。这也就是为什么二者差异的原因了。

2.读写锁

操作

读写锁其实针对读者和写者各一把锁,保证二者的关系。

//初始化
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t*restrict attr);

//销毁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

//加锁和解锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

优先级

int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);
/*
    pref 共有 3 种选择
    PTHREAD_RWLOCK_PREFER_READER_NP (默认设置) 读者优先,可能会导致写者饥饿情况
    PTHREAD_RWLOCK_PREFER_WRITER_NP 写者优先,目前有 BUG,导致表现行为和
    PTHREAD_RWLOCK_PREFER_READER_NP 一致
    PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 写者优先,但写者不能递归加锁
*/

1.读写锁一般默认是读者优先,读者优先说明写者会被强制不允许,也就会出现写者饥饿情况。就是当写者和读者同时运行,那么读者先进入,对数据进行复制

2.当然也可以选择写者优先。不过其顺序是,当前读者有不断进入的,如果碰到写者需要运行,那么当前进入的读者线程先进入完毕,随后写者线程被挂起,优先执行也就进入的读者的读取事件;当全部进入的读者读取完毕,写者再拿取锁,进行写入,期间读者线程被挂起等待。

伪代码实现

定义锁有读者锁rdlock,写者锁wdlock;

读者加锁操作:

pthread_mutex_t rdlock;
int reader_count = 0;

lock(&rdlock);
reader_count++;
if(reader_count==1) lock(&wrlock);
unlock(&rdlock);

//数据读取

lock(&rdlock);
reader_count--;
if(reader_count==0) unlock(&wrlock);
unlock(&rdlock);

1.定义计数器,用于计入有多少读者当前在进行读取数据

2.读取计数前先对读者锁进行上锁,为了达到对计数器的线程安全;此外当reader_count==1,说明此时的一定有读者将来要进行读取数据,那么我们就不允许写者进入访问,所以对写者锁上锁

3.最后读取成功后,需要对读者锁进行上锁,随后减去计数器中读者的个数

写者加锁操作:

lock(&wrlock);

//写者写入

unlock(&wrlock);

相对的写者就简单了不少,直接加锁即可。这样的实现其实就是默认的读者优先。

你可能感兴趣的:(Linux和操作系统,linux,运维,服务器)