React18源码: React调度中的3种优先级类型和Lane的位运算

优先级类型

  • React内部对于优先级的管理,贯穿运作流程的4个阶段(从输入到输出),根据其功能的不同,可以分为3种类型:
    • 1 )fiber优先级(LanePriority)
      • 位于 react-reconciler包,也就是Lane(车道模型)
    • 2 )调度优先级(SchedulerPriority)
      • 位于scheduler包
    • 3 )优先级等级(ReactPriorityLevel)
      • 位于react-reconciler包中的 SchedulerWithReactIntegration.js
      • 负责上述2套优先级体系的转换.
  • Lane 是在 react@17.0.0的新特性.

Lane(车道模型)

  • 英文单词lane翻译成中文表示"车道,航道"的意思,所以很多文章都将Lanes模型称为车道模型

  • Lane模型的源码在 ReactFiberLane.js,源码中大量使用了位运算

  • 首先引入对Lane的解释, 这里简单概括如下:

    • 1 )Lane类型被定义为二进制变量,利用了位掩码的特性,在频繁运算的时候占用内存少,计算速度快.
      • Lane和Lanes就是单数和复数的关系,代表单个任务的定义为Lane,代表多个任务的定义为Lanes
    • 2 )Lane是对于expirationTime的重构,以前使用expirationTime表示的字段,都改为了lane
      renderExpirationTime -> renderlanes
      update.expirationTime -> update.lane
      fiber.expirationTime -> fiber.lanes
      fiber.childExpirationTime -> fiber.childLanes
      root.firstPendingTime and root.lastPendingTime -> fiber.pendingLanes
      
    • 3 )使用Lanes模型相比expirationTime模型的优势
      • Lanes把任务优先级从批量任务中分离出来
      • 可以更方便的判断单个任务与批量任务的优先级是否重叠
      // 判断:单task与batchTask的优先级是否重叠
      // 1.通过expirationTime判断
      const isTaskIncludedInBatch = priorityOfTask >= priorityOfBatch;
      // 2.通过Lanes判断
      const isTaskIncludedInBatch =(task & batchOfTasks) !== 0;// 当同时处理一组任务,该组内有多个任务,且每个任务的优先级不一致
      // 1. 如果通过expirationTime判断,需要维护一个范围(在Lane重构之前,源码中就是这样比较的)
      const isTaskIncludedInBatch =taskPriority <= highestPriorityInRange &&taskPriority >= lowestPriorityInRange;
      // 2. 通过Lanes判断
      const isTaskIncludedInBatch = (task & batchOfTasks) !== 0;
      
  • Lanes使用单个32位二进制变量即可代表多个不同的任务

  • 也就是说一个变量即可代表一个组(group)

  • 如果要在一个group中分离出单个task,非常容易

  • 在expirationTime模型设计之初,react体系中还没有 Suspense 异步渲染的概念

  • 现在有如下场景:有3个任务,其优先级A>B>C,正常来讲只需要按照优先级顺序执行就可以了

  • 但是现在情况变了:A和C任务是CPU密集型,而B是IO密集型(Suspense会调用远程api,算是IO任务)

  • 即A(cpu)>B(IO)>C(cpu).此时的需求需要将任务B从group中分离出来,先处理cpu任务A和C

    //从group中删除或增加task// 通过expirationTime实现
    // 维护一个链表,按照单个task的优先级顺序进行插入
    // 删除单个task(从链表中删除一个元素)
    task. prev.next = task.next;
    //2)增加单个task(需要对比当前task的优先级,插入到链表正确的位置上)
    let current = queue;
    while (task.expirationTime >= current.expirationTime) {current = current.next;
    }
    task.next = current.next;
    current.next = task;
    //3)比较task是否在group中
    const isTaskIncludedInBatch =taskPriority <= highestPriorityInRange &&taskPriority >= lowestPriorityInRang;
    //2.通过Lanes实现
    //1)删除单个task
    batchOfTasks &= ~task;
    //2)增加单个task
    batchOfTasks |= task;
    //3)比较task是否在group中
    const isTaskIncludedInBatch = (task & batchOfTasks) !== 0;
    
  • Lanes是一个不透明的类型,只能在ReactFiberLane.js这个模块中维护

  • 如果要在其他文件中使用,只能通过 ReactFiberLane.js 中提供的工具函数来使用

  • 分析车道模型的源码(ReactFiberLane.js中),可以得到如下结论:

    • 1.可以使用的比特位一共有31位
    • 2.共定义了18种车道(Lane/Lanes)变量,每一个变量占有1个或多个比特位,分别定义为Lane和Lanes类型.
    • 3.每一种车道(Lane/Lanes)都有对应的优先级,所以源码中定义了18种优先级(LanePriority).
    • 4.占有低位比特位的Lane变量对应的优先级越高
      • 最高优先级为 SynclanePriority 对应的车道为
        • Synclane = 0b0000000000000000000000000000001
      • 最低优先级为 OffscreenLanePriority 对应的车道为
        • OffscreenLane = 0b1000000000000000000000000000000

位运算

  • 什么是位运算?程序中的所有数在计算机内存中都是以二进制的形式储存的。

  • 位运算就是直接对整数在内存中的二进制位进行操作。

  • 比如

    • 0 在二进制中用 0 表示,我们用 0000 代表;
    • 1 在二进制中用 1 表示,我们用 0001 代表;
  • 那么先看两个位运算符号 & 和 |

    • & 对于每一个比特位,两个操作数都为 1 时,结果为 1,否则为 0
    • | 对于每一个比特位,两个操作数都为0时,结果为 0,否则为 1
    • 我们看一下两个 1 & 0 和 1 | 0
    • 如上 1 & 0 = 0, 1 | 0 = 1
  • 参考

      0       00001       0001-------------0 & 1 = 0000 = 0
    
  • 再参考

    0       0000
    1       0001
    ----------------
    0 | 1 = 0001 = 1
    

使用一张表格详细说明

运算符用法描述
与 &a & b如果两位都是1则设置每位为1
或 | a | b 如果两位之一为1则设置每位为1
异或 ^a^b如果两位只有一位为1则设置每位为1
非 ~~ a反转操作数的比特位, 即0变成1, 1变成0
左移(<<)a << b 将a的二进制形式向左移b(< 32)比特位, 右边用0填充
有符号右移(>>)a>>b将a的二进制形式向右移b(<32)比特位,丢弃被移除的位,左侧以最高位来填充
无符号右移(>>>)a>>>b将a的二进制形式向右移b(<32)比特位,丢弃被移除的位,并用0在左侧填充

位运算的一个使用场景

  • 比如有一个场景下,会有很多状态常量A,B,C…,这些状态在整个应用中在一些关键节点中做流程控制
  • 比如:
    if(value === A) {// TODO..
    }
    
  • 如上判断value等于常量A,那么进入到if的条件语句中
  • 此时是value属性是简单的一对一关系,但是实际场景下value可能是好几个枚举常量的集合
  • 也就是一对多的关系,那么此时value可能同时代表A和B两个属性
  • 如下图所示:
  • 这时候,如果按照下面的代码来写,就会很麻烦
    if(value === A || value === B) {// TODO..
    }
    
  • 此时的问题就是如何用一个value表示A和B两个属性的集合,这个时候位运算就派上用场了
  • 因为可以把一些状态常量用32位的二进制来表示(这里也可以用其他进制),比如:
    const A = 0b0000000000000000000000000000001 // 优先级最高
    const B = 0b0000000000000000000000000000010
    const C = 0b0000000000000000000000000000100 // 优先级最低
    
  • 通过移位的方式让每一个常量都单独占一位,这样在判断一个属性是否包含常量的时候
  • 可以根据当前位数的1和0来判断
  • 这样如果一个值即代表A又代表B那么就可以通过位运算的 | 来处理
  • 就有 AB = A | B = 0b0000000000000000000000000000011
  • 那么如果把AB的值赋予给value,那么此时的value就可以用来代表A和B
  • 此时当然不能直接通过等于或者恒等来判断value是否为A或者B,此时就可以通过&来判断。具体实现如下:
    const A = 0b0000000000000000000000000000001
    const B = 0b0000000000000000000000000000010
    const C = 0b0000000000000000000000000000100
    const N = 0b0000000000000000000000000000000
    const value = A | B
    console.log((value & A ) !== N) // true
    console.log((value & B ) !== N) // true
    console.log((value & C ) !== N ) // false
    

位运算在 react 中的应用

export const NoLanes = /*                */ 0b0000000000000000000000000000000;
const SyncLane = /*                      */ 0b0000000000000000000000000000001;const InputContinuousHydrationLane = /*  */ 0b0000000000000000000000000000010;
const InputContinuousLane = /*           */ 0b0000000000000000000000000000100;const DefaultHydrationLane = /*          */ 0b0000000000000000000000000001000;
const DefaultLane = /*                   */ 0b0000000000000000000000000010000;const TransitionHydrationLane = /*       */ 0b0000000000000000000000000100000;
const TransitionLane = /*                */ 0b0000000000000000000000001000000;
  • 如上 SyncLane 代表的数值是1,它却是最高的优先级

  • 也即是说lane的代表的数值越小,此次更新的优先级就越大

  • 在新版本的React中,还有一个新特性,就是render阶段可能被中断,在这个期间会产生一个更高优先级的任务

  • 那么会再次更新lane属性,这样多个更新就会合并,这样一个lane可能需要表现出多个更新优先级

  • 我们来看一下React是如何通过位运算分离出优先级的

    function getHighestPriorityLane(lanes) {return lanes & -lanes;
    }
    
  • 如上就是通过 lanes & -lanes 分离出最高优先级的任务的,我们来看一下具体的流程

    • 比如SyncLane和InputContinuousLane合并之后的任务优先级lane为
    • SyncLane = 0b0000000000000000000000000000001
    • InputContinuousLane = 0b0000000000000000000000000000100
    • lane = SyncLane|InputContinuousLane
    • lane = 0b0000000000000000000000000000101
    • 那么通过 lanes & -lanes 分离出 SyncLane
    • 首先我们看一下 -lanes,在二进制中需要用补码表示为:
    • -lane = 0b1111111111111111111111111111011
    • 那么接下来执行 lanes & -lanes 看一下,& 的逻辑是如果两位都是1则设置改位为1,否则为0
    • 那么 lane & -lane, 只有一位(最后一位)全是1,所有合并后的内容为:
    • lane & -lane = 0b0000000000000000000000000000001
  • 可以看得出来lane&-lane的结果是SyncLane,所以通过lane&-lane就能分离出最高优先级的任务

    const SyncLane = 0b0000000000000000000000000000001
    const InputContinuousLane = 0b0000000000000000000000000000100
    const lane = SyncLane | InputContinuousLane
    console.log((lane & -lane) === SyncLane ) // true
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/697483.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

掌握 Android 中的 RecyclerView 优化

掌握 Android 中的 RecyclerView 优化 一、RecyclerView Pool以及何时使用它二、onCreateViewHolder 和 onBindViewHolder三、优化 RecyclerView 的不同方法四、视图无效与请求布局五、ViewHolder模式六、默认的废料和脏视图类型七、结论 RecyclerView 是 Android 中一个功能强…

【操作系统】磁盘存储空间的管理

实验5 磁盘存储空间的管理 一、实验目的 磁盘是用户存放程序和数据的存储设备&#xff0c;磁盘管理的主要目的是充分有效地利用磁盘空间。本实验模拟实现磁盘空间的分配与回收&#xff0c;使学生对磁盘空间的管理有一个较深入的理解。 二、实验内容 实验任务&#xff1a;用位…

FreeSWITCH debian11/12 apt安装

官方给的easy way安装方式如下&#xff1a; # 换成自己的token TOKENpat_ZrPXJQ8JNWsVQW2ubhnUwi7gapt-get update && apt-get install -y gnupg2 wget lsb-releasewget --http-usersignalwire --http-password$TOKEN -O /usr/share/keyrings/signalwire-freeswitch-…

#LLM入门|Prompt#1.2_提示原则_Guidelines

提示原则 一、编写清晰、具体的指令 使用分隔符清晰地表示输入的不同部分&#xff1a; 在Prompt中使用分隔符&#xff0c;如、“”"、< >、 、:等&#xff0c;将不同的文本部分区分开来&#xff0c;避免混淆和意外的结果。分隔符能够防止提示词注入&#xff0c;提…

petalinux_zynq7 驱动DAC以及ADC模块之四:python实现http_api

前文&#xff1a; petalinux_zynq7 C语言驱动DAC以及ADC模块之一&#xff1a;建立IPhttps://blog.csdn.net/qq_27158179/article/details/136234296petalinux_zynq7 C语言驱动DAC以及ADC模块之二&#xff1a;petalinuxhttps://blog.csdn.net/qq_27158179/article/details/1362…

Java观察者模式:实现高效的事件驱动编程

Java中的装饰者模式&#xff1a;灵活地为对象添加功能 一、引言 在软件设计中&#xff0c;我们经常需要为对象动态地添加功能或行为。装饰者模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许我们在运行时将功能动态地添加到对象上&…

串的相关题目

于是他错误的点名开始了 我发现有关hash得题目有些是可以通过map数组来完成的&#xff1a;何为map数组&#xff0c;我们先思考一下最简单的桶的排序&#xff0c;桶排序是将我们需要数字最为下标输进数组中&#xff0c;而数组是存放的数字是这个数字出现的次数&#xff0c;但是由…

Matlab论文插图绘制模板第137期—极坐标分组气泡图

在之前的文章中&#xff0c;分享了Matlab极坐标气泡图的绘制模板&#xff1a; 进一步&#xff0c;再来分享一下极坐标分组气泡图。 先来看一下成品效果&#xff1a; ​ 特别提示&#xff1a;本期内容『数据代码』已上传资源群中&#xff0c;加群的朋友请自行下载。有需要的朋…

解决SSH远程登录开饭板出现密码错误问题

输入“adduser Zhanggong回车”&#xff0c;使用adduser命令创建开发板用户名为Zhanggong 输入密码“123456” 输入密码“123456”

openGauss学习笔记-226 openGauss性能调优-系统调优-配置LLVM-LLVM适用场景与限制

文章目录 openGauss学习笔记-226 openGauss性能调优-系统调优-配置LLVM-LLVM适用场景与限制226.1 适用场景226.2 非适用场景 openGauss学习笔记-226 openGauss性能调优-系统调优-配置LLVM-LLVM适用场景与限制 226.1 适用场景 支持LLVM的表达式 查询语句中存在以下的表达式支持…

PostgreSQL数据库备份和恢复

一、数据库备份 /usr/lib/postgresql/16/bin/pg_dump -h localhost -p 5432 -U odoo -F c -b -v -f backup.sql laonian 二、数据库恢复 1 现在目标pgsql数据库中创建空数据库老年 create database laonian owner odoo; 2 执行恢复命令&#xff08;windows系统下&#xff…

网络安全-nc(Netcat)工具详解

经常在反弹shell的时候使用nc命令&#xff0c;但是从来没有了解过&#xff0c;今天翻书看到了&#xff0c;准备记录一下。 nc全称Netcat&#xff0c;是TCP/IP连接的瑞士军刀。哈哈我最喜欢瑞士军刀了。 有一个比较偏的知识点&#xff0c;nc还可以探测目标的端口是否开放&…

Modern C++ std::variant的5个特性+原理

1 前言 上一节《Modern C std::variant的实现原理》我们简单分析了std::variant的实现原理&#xff0c;其实要学好C编程&#xff0c;除了看优秀的代码包括标准库实现&#xff0c;读文档也是很便捷且必须的一种办法。 本节我将逐条解析文档中的五个特性&#xff0c;解析的办法有…

LINUX操作系统:重定向

输出重定向&#xff1a;将命令行程序的输出重定向到其他位置&#xff0c;如文件、程序、打印机等。 输入重定向&#xff1a;从其他位置获取输入&#xff0c;而不是从标准输入&#xff08;键盘、鼠标等&#xff09; 错误重定向&#xff1a;同输出。 输出重定向&#xff08;Outp…

R语言【sp】——over(),%over%

Package sp version 1.5-0 Description 点、网格和多边形的一致空间覆盖:在对象x的空间位置从空间对象y检索索引或属性。 Usage over(x, y, returnList = FALSE, fn = NULL, ...) x %over% y Arguments 参数【x】:查询的几何(位置)。 参数【y】:层,从中查询几何或属性。…

PYTHON-使用正则表达式进行模式匹配

目录 Python 正则表达式Finding Patterns of Text Without Regular ExpressionsFinding Patterns of Text with Regular ExpressionsCreating Regex ObjectsMatching Regex ObjectsReview of Regular Expression MatchingMore Pattern Matching with Regular ExpressionsGroupi…

阿里开源低代码引擎 - Low-Code Engine

阿里开源低代码引擎 - Low-Code Engine 本文主要介绍如何在Windows运行/开发阿里开源低代码引擎 - Low-Code Engine 详细文档参见【 阿里开源低代码引擎 - Low-Code Engine 官方文档】 目录 阿里开源低代码引擎 - Low-Code Engine一、环境准备1、使用 WSL 在 Windows 上安装 L…

方法鉴权:基于 Spring Aop 的注解鉴权

在Spring框架中&#xff0c;可以使用面向切面编程&#xff08;AOP&#xff09;来实现注解鉴权。这通常涉及到定义一个切面&#xff08;Aspect&#xff09;&#xff0c;该切面会在方法执行前进行拦截&#xff0c;并根据注解value值来决定是否允许执行该方法。 简单思路&#xf…

Java学习笔记2024/2/22

面向对象进阶部分学习方法&#xff1a; 特点&#xff1a; 逻辑性没有那么强&#xff0c;但是概念会比较多。 记忆部分重要的概念&#xff0c;理解课堂上讲解的需要大家掌握的概念&#xff0c;多多练习代码。 今日内容 复习回顾 static关键字 继承 教学目标 能够掌握st…

【开源】JAVA+Vue.js实现医院门诊预约挂号系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 功能性需求2.1.1 数据中心模块2.1.2 科室医生档案模块2.1.3 预约挂号模块2.1.4 医院时政模块 2.2 可行性分析2.2.1 可靠性2.2.2 易用性2.2.3 维护性 三、数据库设计3.1 用户表3.2 科室档案表3.3 医生档案表3.4 医生放号…