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的时间。
库语言的实现
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);
相对的写者就简单了不少,直接加锁即可。这样的实现其实就是默认的读者优先。