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.pswp.cn/news/697483.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/697483.shtml
英文地址,請注明出處:http://en.pswp.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 醫生放號…