iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > html >Libtask源码解析之如何理解锁
  • 608
分享到

Libtask源码解析之如何理解锁

2024-04-02 19:04:59 608人浏览 八月长安
摘要

这篇文章主要讲解了“Libtask源码解析之如何理解锁”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Libtask源码解析之如何理解锁”吧!libtask中

这篇文章主要讲解了“Libtask源码解析之如何理解”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Libtask源码解析之如何理解锁”吧!

libtask中其实不需要锁,因为libtask中协程是非抢占式的,不存在竞态条件。但是libtask还是实现了一套锁的机制。我们看一下这个锁机制的实现。首先我们看一下结构体。

struct QLock {     // 锁持有者     Task    *owner;     // 等待该锁的队列     Tasklist waiting; };

接着我们看一下锁的操作。

加锁

static int _qlock(QLock *l, int block) {         // 锁没有持有者,则置当前协程为持有者,直接返回,1表示加锁成功     if(l->owner == nil){         l->owner = taskrunning;         return 1;     }     // 非阻塞,则直接返回,0表示加锁失败     if(!block)         return 0;     // 插入等待锁队列     addtask(&l->waiting, taskrunning);     taskstate("qlock");     // 切换到其他协程     taskswitch();     // 切换回来时,如果持有锁的协程不是当前协程,则异常退出,因为只有持有锁才会被切换回来,见unqlock     if(l->owner != taskrunning){         fprint(2, "qlock: owner=%p self=%p oops\n", l->owner, taskrunning);         abort();     }     return 1; }

如果当前锁没有持有者,则当前协程X就变成锁的持有者,否则把协程X插入等待锁队列中,然后让出cpu,切换到其他协程。当后续锁被释放并被协程X持有时,协程X就会被唤醒继续持续。加锁可以分为阻塞和非阻塞两种模式。非阻塞就是加锁失败也不会切换协程。

// 阻塞式加锁 void qlock(QLock *l) {     _qlock(l, 1); }  // 非阻塞式加锁 int canqlock(QLock *l) {     return _qlock(l, 0); }

释放锁

接下来我们看一下释放锁的逻辑

// 释放锁 void qunlock(QLock *l) {     Task *ready;     // 锁并没有持有者,异常退出     if(l->owner == 0){         fprint(2, "qunlock: owner=0\n");         abort();     }     // 如果还有协程在等待该锁,则置为持有者,并且从等待队列中删除,然后修改状态为就绪并加入就绪队列     if((l->owner = ready = l->waiting.head) != nil){         deltask(&l->waiting, ready);         taskready(ready);     } }

当锁被释放时,如果还有协程在等待该锁,则从等待队列中摘取一个节点,然后变成锁的持有者并从等待队列中删除。最后插入就绪队列等待调度。以上是一种互斥锁的实现。下面我们再来看一下读写锁机制,读写锁也是互斥的,但是在某些情况下也可以共享。我们看一下读写锁的数据结构

struct RWLock {     // 正在读的读者个数     int    readers;     // 当前正在写的写者,只有一个     Task    *writer;     // 等待读和写的队列     Tasklist rwaiting;     Tasklist wwaiting; };

接着我看一下加锁逻辑。

加读锁

// 加读锁 static int _rlock(RWLock *l, int block) {              if(l->writer == nil && l->wwaiting.head == nil){         l->readers++;         return 1;     }     // 非阻塞则直接返回     if(!block)         return 0;     // 插入等待读队列     addtask(&l->rwaiting, taskrunning);     taskstate("rlock");     // 切换上下文     taskswitch();     // 切换回来了,说明加锁成功     return 1; }

当且仅当没有正在写的写者和等待写的写者时,才能加读锁成功,否则根据加锁模式进行下一步处理,直接返回加锁失败或者插入等待队列,然后切换到其他协程。我们看到当有一个等待写的协程时(l->wwaiting.head  != nil),则后续的读者就无法加锁成功,而是被插入等待队列,否则可能会引起写者饥饿。

加写锁

// 加写锁 static int _wlock(RWLock *l, int block) {         // 没有正在写并且没有正在读,则加锁成功,并置写者为当前协程     if(l->writer == nil && l->readers == 0){         l->writer = taskrunning;         return 1;     }     // 非阻塞则直接返回     if(!block)         return 0;     // 加入等待写队列     addtask(&l->wwaiting, taskrunning);     taskstate("wlock");     // 切换     taskswitch();     // 切换回来说明拿到锁了     return 1; }

当且仅当没有正在写的写者和没有正在读的读者时,才能加写锁成功。否则类似加读锁一样处理。

释放读锁

// 释放读锁 void runlock(RWLock *l) {     Task *t;     // 读者减一,如果等于0并且有等待写的协程,则队列第一个协程持有该锁     if(--l->readers == 0 && (t = l->wwaiting.head) != nil){         deltask(&l->wwaiting, t);         l->writer = t;         taskready(t);     } }

持有读锁,说明当前肯定没有正在写的写者,但是可能有等待写的写者和等待读的读者(因为有等待写的写者导致无法加锁成功)。当释放读锁时,如果还有其他读者,则其他读者可以继续持有锁,因为读者可以共享读锁,而写者保持原来状态。如果这时候没有读者但是有等待写的写者,则从队列中选择第一个节点成为锁的持有者,其他的写者则继续等待,因为写者不能共享写锁。

释放写锁

// 释放写锁 void wunlock(RWLock *l) {     Task *t;     // 没有正在写,异常退出     if(l->writer == nil){         fprint(2, "wunlock: not locked\n");         abort();     }     // 置空,没有协程正在写     l->writer = nil;     // 有正在读,异常退出,写的时候,是无法读的     if(l->readers != 0){         fprint(2, "wunlock: readers\n");         abort();     }     // 释放写锁时,优先让读者持有锁,因为读者可以共享持有锁,提高并发     // 读可以共享,把等待读的协程都加入就绪队列,并持有锁     while((t = l->rwaiting.head) != nil){         deltask(&l->rwaiting, t);         l->readers++;         taskready(t);     }     // 释放写锁时,如果又没有读者,并且有等待写的协程,则队列的第一个等待写的协程持有锁     if(l->readers == 0 && (t = l->wwaiting.head) != nil){         deltask(&l->wwaiting, t);         l->writer = t;         taskready(t);     } }

持有写锁,可能有等待写的写者和等待读的读者。这里是读者优先持有锁,因为读者可以共享持有锁,提高并发,如果没有读者,则再判断写者。

感谢各位的阅读,以上就是“Libtask源码解析之如何理解锁”的内容了,经过本文的学习后,相信大家对Libtask源码解析之如何理解锁这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: Libtask源码解析之如何理解锁

本文链接: https://www.lsjlt.com/news/83593.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Libtask源码解析之如何理解锁
    这篇文章主要讲解了“Libtask源码解析之如何理解锁”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Libtask源码解析之如何理解锁”吧!libtask中...
    99+
    2024-04-02
  • Redisson分布式锁之加解锁源码分析
    这篇文章主要介绍“Redisson分布式锁之加解锁源码分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Redisson分布式锁之加解锁源码分析”文章能帮助大家解决问题。锁的可重入性我们都知道,Ja...
    99+
    2023-07-05
  • 如何理解ReentrantLock非公平锁源码
    这篇文章主要讲解了“如何理解ReentrantLock非公平锁源码”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解Ree...
    99+
    2024-04-02
  • MyBatis3源码解析之如何获取数据源详解
    目录前言jdbc传统JDBC弊端思考源码分析获取数据源总结前言 上文讲的MyBatis部署运行且根据官网运行了一个demo:一步到位部署运行MyBatis3源码<保姆级>...
    99+
    2024-04-02
  • 如何理解Redisson分布式锁的源码
    本篇内容介绍了“如何理解Redisson分布式锁的源码”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Red...
    99+
    2024-04-02
  • Mybatis源码解析之事务管理
    目录Mybatis事务管理和Spring整合后的事务管理Mybatis事务管理 我们可以在mybatis-config.xml中配置事务管理器的实现 <transactio...
    99+
    2024-04-02
  • Spring源码解析之Configuration
    目录一、@Configuration1.1 未加@Configuration1.2 加上@Configuration1.3 Cglib动态代理二、源码跟踪2.1 Annotation...
    99+
    2024-04-02
  • Java源码解析之LinkedHashMap
    目录一、成员变量二、构造函数三、重要方法一、成员变量 先来看看存储元素的结构吧: static class Entry<K,V> extends HashMap.No...
    99+
    2024-04-02
  • Java源码解析之ConcurrentHashMap
    早期 ConcurrentHashMap,其实现是基于: 分离锁,也就是将内部进行分段(Segment),里面则是 HashEntry 的数组,和 HashMap 类似,哈...
    99+
    2024-04-02
  • Java源码解析之ClassLoader
    目录一、前言二、java 中的 ClassLoader三、Android 中的 ClassLoader四、双亲委派机制五、源码分析一、前言 一个完整的Java应用程序,当程序在运行时...
    99+
    2024-04-02
  • Java源码解析之详解ReentrantLock
    ReentrantLock ReentrantLock是一种可重入的互斥锁,它的行为和作用与关键字synchronized有些类似,在并发场景下可以让多个线程按照一定的顺序访问同一资...
    99+
    2024-04-02
  • Java源码解析之详解ImmutableMap
    一、案例场景 遇到过这样的场景,在定义一个static修饰的Map时,使用了大量的put()方法赋值,就类似这样—— public static final Map<St...
    99+
    2024-04-02
  • OpenJDK源码解析之System.out.println详解
    目录一、前戏二、JVM源码分析三、坑?四、总结一、前戏 可能不少小伙伴习惯在代码中使用sout打印一些信息,就像这样: System.out.println("hello wor...
    99+
    2024-04-02
  • Java基础之如何理解Object源码
    本篇内容主要讲解“Java基础之如何理解Object源码”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java基础之如何理解Object源码”吧!getClasspublic fina...
    99+
    2023-06-15
  • SDWebImage源码解析之SDWebImageManager的注解
    ...
    99+
    2023-06-04
  • 阻塞队列之如何理解LinkedBlockingQueue源码
    本篇内容介绍了“阻塞队列之如何理解LinkedBlockingQueue源码”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读...
    99+
    2024-04-02
  • Tomcat源码解析之Web请求与处理
    目录前言一、EndPoint二、ConnectionHandler三、Coyote四、容器责任链模式前言 Tomcat最全UML类图 Tomcat请求处理过程: Connecto...
    99+
    2024-04-02
  • 如何理解Mybatis源码
    本篇内容介绍了“如何理解Mybatis源码”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!为什么纠结因为面试...
    99+
    2024-04-02
  • 如何理解ArrayList源码
    本篇内容主要讲解“如何理解ArrayList源码”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何理解ArrayList源码”吧!ArrayList类图如下:A...
    99+
    2024-04-02
  • Redis Lua同步锁实现源码解析
    目录Redis+Lua同步锁Jedis配置Jedis工具类→获取jedisredis 锁工具类加锁示例(jedis+lua)Redis+Lua同步锁 Jedis配置 @Configuration @Getter...
    99+
    2023-05-19
    Redis Lua同步锁 Redis Lua 源码解析
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作