Lock 锁的实现
锁的种类
自旋锁(spinlock):无法获得锁,就一直循环获取,适合短时间的加锁
睡眠锁(sleeplock):为了防止长时间的循环等待,在获取不到锁时,进程陷入睡眠,当锁释放时对睡眠进程进行唤醒
自旋锁的实现
其实自旋锁的实现很简单,不过是一个状态量置1或者置0的操作
为了防止中断产生死锁以及编译器将临界区的指令重排到锁操作外,使用一些特殊指令
在修改状态量时,使用原子操作确保不会出现操作过程中,其他操作发生。
// 自旋锁的数据结构
// Mutual exclusion lock.
struct spinlock {
uint locked; // Is the lock held?
// For debugging:
char *name; // Name of lock.
struct cpu *cpu; // The cpu holding the lock.
};
// 辅助函数
// Check whether this cpu is holding the lock.
// Interrupts must be off.
int
holding(struct spinlock *lk)
{
int r;
// 锁处于被持有状态并且持有cpu为当前cpu 返回 1(ture)
r = (lk->locked && lk->cpu == mycpu());
return r;
}
// 加锁操作
// Acquire the lock.
// Loops (spins) until the lock is acquired.
void
acquire(struct spinlock *lk)
{
// 关中断
// 加解锁的流程中需要关闭中断
// 防止使用锁的过程中中断,而中断处理程序又需要锁,造成死锁
push_off(); // disable interrupts to avoid deadlock.
// 判读当前cpu是否正在持有该锁
// 如果当前CPU在其他位置持有了这个锁,那么当前的加锁操作将永远无法完成
// 程序会阻塞在当前位置,并且其他位置持有锁的位置也无法释放
// 程序将进入死锁状态
if(holding(lk))
panic("acquire");
// __sync_lock_test_and_set(&lk->locked, 1)
// 加锁的原子操作,确保括号中的操作为原子操作,一般是使用CPU的特殊硬件指令实现
// 效果:如果lk->locked等于0,我们调用test-and-set将1写入locked字段,并且返回locked字段之前的数值0。
// 如果lk->locked等于1,则返回1
// On RISC-V, sync_lock_test_and_set turns into an atomic swap:
// a5 = 1
// s1 = &lk->locked
// amoswap.w.aq a5, a5, (s1)
while(__sync_lock_test_and_set(&lk->locked, 1) != 0)
;
// __sync_synchronize()
// 从当前位置到下一个__sync_synchronize指令之间禁止指令重排
// 加锁和critical section的代码执行通常完全相互独立,它们之间没有任何关联
// 因此CPU和编译器极有可能将critical section代码置于锁之外
// 对于synchronize指令,任何在它之前的load/store指令,都不能移动到它之后
// Tell the C compiler and the processor to not move loads or stores
// past this point, to ensure that the critical section's memory
// references happen strictly after the lock is acquired.
// On RISC-V, this emits a fence instruction.
__sync_synchronize();
// Record info about lock acquisition for holding() and debugging.
lk->cpu = mycpu();
}
// 释放锁的操作
// Release the lock.
void
release(struct spinlock *lk)
{
if(!holding(lk))
panic("release");
lk->cpu = 0;
// Tell the C compiler and the CPU to not move loads or stores
// past this point, to ensure that all the stores in the critical
// section are visible to other CPUs before the lock is released,
// and that loads in the critical section occur strictly before
// the lock is released.
// On RISC-V, this emits a fence instruction.
__sync_synchronize();
// __sync_lock_release()
// 原子性的将lk->locked置0
// Release the lock, equivalent to lk->locked = 0.
// This code doesn't use a C assignment, since the C standard
// implies that an assignment might be implemented with
// multiple store instructions.
// On RISC-V, sync_lock_release turns into an atomic swap:
// s1 = &lk->locked
// amoswap.w zero, zero, (s1)
__sync_lock_release(&lk->locked);
pop_off();
}
睡眠锁的实现
// 睡眠锁的数据结构
struct sleeplock {
uint locked; // Is the lock held?
struct spinlock lk; // spinlock protecting this sleep lock
// For debugging:
char *name; // Name of lock.
int pid; // Process holding lock
};
void
acquiresleep(struct sleeplock *lk)
{
acquire(&lk->lk);
while (lk->locked) {
sleep(lk, &lk->lk);
}
lk->locked = 1;
lk->pid = myproc()->pid;
release(&lk->lk);
}
void
releasesleep(struct sleeplock *lk)
{
acquire(&lk->lk);
lk->locked = 0;
lk->pid = 0;
wakeup(lk);
release(&lk->lk);
}
// Atomically release lock and sleep on chan.
// Reacquires lock when awakened.
// sleep函数设置当前进程的chan(表示等待该chan的信息),
// 然后将进程设置为SLEEPING状态,放弃CPU,进入进程调度
// 被唤醒后,将继续执行sched()函数后面指令,将chan置空
void
sleep(void *chan, struct spinlock *lk)
{
struct proc *p = myproc();
// Must acquire p->lock in order to
// change p->state and then call sched.
// Once we hold p->lock, we can be
// guaranteed that we won't miss any wakeup
// (wakeup locks p->lock),
// so it's okay to release lk.
acquire(&p->lock); //DOC: sleeplock1
release(lk);
// Go to sleep.
p->chan = chan;
p->state = SLEEPING;
sched();
// Tidy up.
p->chan = 0;
// Reacquire original lock.
release(&p->lock);
acquire(lk);
}
// Wake up all processes sleeping on chan.
// Must be called without any p->lock.
// wakeup函数将所有在当前chan等待的进程状态全部设置成RUNNABLE
void
wakeup(void *chan)
{
struct proc *p;
for(p = proc; p < &proc[NPROC]; p++) {
if(p != myproc()){
acquire(&p->lock);
if(p->state == SLEEPING && p->chan == chan) {
p->state = RUNNABLE;
}
release(&p->lock);
}
}
}
为什么睡眠锁的实现需要使用自旋锁?
待续
Lock 锁的实现的更多相关文章
- Lock锁的使用示例
Lock锁是java5用来代替synchronized的一种面向对象的锁的方案 public class LockDemo { /** * Lock是用来替换synchronized, 优点是Lock ...
- Android(java)学习笔记69:JDK5之后的Lock锁的概述和使用
1. Lock锁的概述: java.util.concurrent.locks,接口Lock 首先Lock是一个接口,Lock实现提供了比使用synchronized方法 和 同步代码块更为广泛的锁定 ...
- python多线程threading.Lock锁用法实例
本文实例讲述了python多线程threading.Lock锁的用法实例,分享给大家供大家参考.具体分析如下: python的锁可以独立提取出来 mutex = threading.Lock() #锁 ...
- Lock锁_线程_线程域
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...
- (删)Java线程同步实现二:Lock锁和Condition
在上篇文章(3.Java多线程总结系列:Java的线程同步实现)中,我们介绍了用synchronized关键字实现线程同步.但在Java中还有一种方式可以实现线程同步,那就是Lock锁. 一.同步锁 ...
- 转: 【Java并发编程】之二十:并发新特性—Lock锁和条件变量(含代码)
简单使用Lock锁 Java5中引入了新的锁机制--Java.util.concurrent.locks中的显式的互斥锁:Lock接口,它提供了比synchronized更加广泛的锁定操作.Lock接 ...
- 使用Lock锁生产者消费者模式
package com.java.concurrent; import java.util.concurrent.locks.Condition; import java.util.concurren ...
- JAVA基础再回首(二十五)——Lock锁的使用、死锁问题、多线程生产者和消费者、线程池、匿名内部类使用多线程、定时器、面试题
JAVA基础再回首(二十五)--Lock锁的使用.死锁问题.多线程生产者和消费者.线程池.匿名内部类使用多线程.定时器.面试题 版权声明:转载必须注明本文转自程序猿杜鹏程的博客:http://blog ...
- java并发编程的艺术——第五章总结(Lock锁与队列同步器)
Lock锁 锁是用来控制多个线程访问共享资源的方式. 一般来说一个锁可以防止多个线程同时访问共享资源(但有些锁可以允许多个线程访问共享资源,如读写锁). 在Lock接口出现前,java使用synchr ...
- Java中的Lock锁
Lock锁介绍: 在java中可以使用 synchronized 来实现多线程下对象的同步访问,为了获得更加灵活使用场景.高效的性能,java还提供了Lock接口及其实现类ReentrantLock和 ...
随机推荐
- Django笔记&教程 6-4 forms进阶操作,重写forms方法
Django 自学笔记兼学习教程第6章第4节--forms进阶操作,重写forms方法 点击查看教程总目录 1 - 重写clean方法 在本章第三节中,我们举了个注册学生账号的例子,里面我们使用模型来 ...
- requests的post请求基本使用
import requests # 请求url url = 'https://fanyi.baidu.com/sug' # 请求头 headers = { 'User-Agent': 'Mozilla ...
- robotframework-autoitlibrary离线安装
由于AutoItLibrary需要依赖pywin32库.所以要使用AutoItLibrary必须要先安装好pywin32 1.pywin32下载地址安装:http://sourceforge.net/ ...
- 【Sass/SCSS】预加载器中的“轩辕剑”
[Sass/SCSS]预加载器中的"轩辕剑" 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 说明 随着前端 ...
- vue开发中的一些简单骚操作
在开发过程中,我们可以定义很多参数,这时需要通过不同的操作来改变不同的参数,这就比较复杂了, 虽然不难,但是代码多了也不好看,这时我们就可以通过简单的操作就行简化: 1.对象使用方括号 let obj ...
- Roslyn 编译器Api妙用:动态生成类并实现接口
在上一篇文章中有讲到使用反射手写IL代码动态生成类并实现接口. 反射的妙用:C#通过反射动态生成类型继承接口并实现 有位网友推荐使用 Roslyn 去脚本化动态生成,今天这篇文章就主要讲怎么使用 Ro ...
- Java设计模式之(三)——建造者模式
1.什么是建造者模式 Separate the construction of a complex object from its representation so that the same co ...
- Water 2.4 发布,一站式服务治理平台
Water(水孕育万物...) Water 为项目开发.服务治理,提供一站式解决方案(可以理解为微服务架构支持套件).基于 Solon 框架开发,并支持完整的 Solon Cloud 规范:已在生产环 ...
- [bzoj1853]幸运数字
容易发现幸运数字只有1024个,暴力标记倍数还是会tle的 容斥,即从中任选i个的lcm,复杂度为$o(2^1024)$ 剪枝一:当答案超过1024就不用算了 剪枝二:当某个数是另一个数的倍数时就删掉 ...
- Redis线程模型的前世今生
一.概述 众所周知,Redis是一个高性能的数据存储框架,在高并发的系统设计中,Redis也是一个比较关键的组件,是我们提升系统性能的一大利器.深入去理解Redis高性能的原理显得越发重要,当然Red ...