為什么我的UI界面會突然卡頓,失去響應

有操作都應是“非阻塞”的,以確保能隨時響應用戶的輸入。導致主線程阻塞的常見“元兇”主要涵蓋五個方面:主線程被“長時間”的同步計算所“阻塞”、單次渲染的界面節點過多或過于復雜、內存中存在“未釋放”的巨大對象或“內存泄漏”、響應了“高頻率”觸發的事件而未做“節流”或“防抖”、以及后臺的“網絡請求”或“數據處理”邏輯設計不當

其中,主線程被長時間的同步計算所阻塞,是問題的根源。絕大多數圖形用戶界面系統,都運行在一個“單線程”模型上,這個唯一的線程,既要負責繪制界面,又要負責響應用戶的點擊、滾動等操作。如果開發者,在這個線程上,啟動了一項耗時巨大的計算(例如,處理一個巨大的數據數組),那么,這個線程,就會被完全地、毫無保留地,被這個計算任務所“霸占”,直到它完成為止。在此期間,所有新的用戶操作,都會被“無視”,界面因此“凍結”,從而給用戶帶來“卡死”的直觀感受。

一、問題的“本質”、單線程的“宿命”

要深刻理解“界面卡頓”的根源,我們必須首先,理解現代圖形用戶界面,其內在的、底層的“工作模型”。這個模型,可以被概括為“單線程 + 事件循環”。

1. 圖形用戶界面的“單線程”模型

在幾乎所有我們日常接觸的圖形用戶界面框架中——無論是網頁瀏覽器、安卓、蘋果的操作系統,還是各種桌面應用框架——所有與“界面”相關的操作,包括繪制界面元素、響應用戶的鼠標點擊和鍵盤輸入、執行動畫等,都被嚴格地,限定在了一個唯一的、專用的“主線程”上執行

之所以采用這種“單線程”的設計,主要是為了簡化狀態管理,避免復雜的并發問題。如果允許多個線程,在同一時刻,隨意地,去修改同一個界面元素(例如,線程A想把它變紅,線程B想把它變綠),那么,整個界面的狀態,將很快地,陷入一種不可預測的、混亂的“競態條件”之中。

2. 事件循環的工作原理

這個唯一的“主線程”,其內部,運轉著一個永不停止的“事件循環”。我們可以將其,理解為一個極其勤勉的“調度中心”,它在周而復始地,執行著一個簡單的循環:

檢查“任務隊列”:去一個名為“任務隊列”的“信箱”里,看看是否有新的“任務”到達。這些“任務”,可以是“用戶剛剛點擊了一下按鈕”、“一個網絡數據包已經下載完成”、“一個定時器的時間到了”等等。

取出一個任務并執行:如果隊列中有任務,就取出最老的一個,并完整地、從頭到尾地,執行與這個任務相關的所有代碼。

進行界面“渲染”:在該任務執行完畢后,檢查,是否有任何代碼,請求了對界面的修改。如果有,就進行一次統一的、重繪屏幕的操作。

返回第一步,繼續檢查任務隊列。

3. “阻塞”的科學定義

界面的“卡頓”或“失去響應”,在技術層面上,其唯一的、直接的原因,就是上述“事件循環”的第二步——“取出一個任務并執行”——其耗費的時間,過長了

根據谷歌等行業領導者提出的RAIL性能模型,如果一個任務的執行時間,超過了100毫秒,人類用戶,就能明確地,感受到“延遲”。如果,這個時間,超過了500毫秒甚至1秒,那么,用戶,就會主觀地,判定這個程序已經“卡死”了。因為,在這個耗時的任務,執行完畢之前,整個“事件循環”,都被“阻塞”在了第二步。它既沒有機會,去處理任何新的、后續的用戶輸入(如你的第二次點擊),也沒有機會,去執行任何屏幕的重繪。

二、元兇一、主線程的“同步”計算過載

這是導致主線程阻塞的、最直接、也最常見的“罪魁禍首”。

1. 場景一:復雜的數據處理

問題描述:開發者,從一個接口,獲取到了一個包含了數萬條記錄的、巨大的數據數組。然后,在一個響應用戶點擊的回調函數中,直接地,對這個數組,進行了一次復雜的、多重嵌套的“循環、過濾、排序、轉換”的同步操作。

后果:這個數據處理的循環,可能會持續執行數百毫秒甚至數秒。在此期間,主線程被完全“凍結”,界面對用戶的任何后續操作,都“毫無反應”。

2. 場景二:耗時的算法

問題描述:在主線程上,直接地,執行一些計算密集型的算法,例如,復雜的路徑規劃、圖像的濾鏡處理、數據的加密或解密等。

【解決方案】將所有“耗時計算”,都從“主線程”,轉移到“后臺線程”

Web Worker:在網頁開發中,Web Worker技術,允許我們,創建一個或多個,完全獨立于主線程的“后臺線程”。我們可以將那些計算密集型的、與界面無直接關系的數據處理任務,都“派發”給這些后臺線程去完成。后臺線程,在計算完畢后,再通過一個異步的消息,將最終的“結果”,安全地,返回給主線程。主線程,則只負責,接收這個“結果”,并用它來更新界面。

異步化拆分:對于一些無法,或不方便,被完全轉移到后臺線程的任務,我們可以采用一種“化整為零”的策略。即將一個“大的”同步任務,人為地,拆分為多個“小的”任務塊,并利用setTimeout(..., 0)這樣的技巧,將每一個小任務塊,都作為一個新的、獨立的“宏任務”,重新調度到“任務隊列”的末尾。這使得,在每兩個小任務塊的執行間隙,主線程,都有機會“喘息”,去處理其他的用戶輸入和界面渲染。

三、元兇二、渲染的“性能”瓶頸

有時,卡頓,并非源于“計算”,而是源于“繪制”本身。

一次性渲染“過多”的元素:當一個需求,要求,在一個頁面上,同時,渲染一個包含數千行、數十列的巨大表格時,瀏覽器,需要,在短時間內,創建數萬個獨立的界面元素節點,并為它們,逐一地,計算樣式和布局。這個過程,本身,就可能耗時數秒,導致界面“白屏”或“卡頓”。

頻繁且無效的“重繪”與“回流”:在網頁中,界面的渲染,分為“回流”(計算元素的位置和幾何形狀)和“重繪**(填充元素的像素顏色和樣式)兩個階段。“回流”的成本,遠高于“重繪”。如果我們的代碼,在一次操作中,反復地,去修改那些會“影響布局”的屬性(例如,一個元素的寬度、高度、或邊距),那么,就會,觸發多次的、昂貴的、全局性的“回流”,導致嚴重的性能問題。

【解決方案】

虛擬化/窗口化:對于需要展示海量數據的長列表或表格,我們不應,一次性地,將所有數據,都渲染出來。而應采用“虛擬化”技術,即,只渲染那些,當前,在用戶的“可視窗口”內,所能看到的、少數的幾十個元素。當用戶滾動時,再動態地,計算和渲染新的可見元素。

請求動畫幀:對于需要,實現流暢動畫的場景,應使用requestAnimationFrame,來將我們的動畫邏輯,與瀏覽器的“刷新頻率”,進行同步,以獲得最佳的性能。

優化樣式操作:盡量避免,在JavaScript中,頻繁地,讀寫會觸發“回流”的樣式屬性。

四、元兇三、高頻事件的“處理風暴”

“瘋狂”的事件觸發:在界面中,存在一些“高頻”事件,例如,scroll(滾動)、mousemove(鼠標移動)、resize(窗口大小調整)。這些事件,可以在短短一秒鐘內,被觸發數十次甚至上百次

問題的根源:如果,我們為這些“高頻”事件,綁定了一個處理邏輯相對復雜的“事件回調函數”,那么,這個回調函數,就會被“瘋狂地”,在一秒鐘內,執行數十上百次。這會迅速地,將“任務隊列”,填滿,并耗盡中央處理器的計算能力,導致界面,在滾動或拖拽時,出現明顯的“掉幀”和“卡頓”。

【解決方案】為高頻事件的回調函數,配備“減速器”

函數防抖:其策略是,“稍等一下,等你不動了,我再處理”。它會強制一個函數,在某個連續的操作結束后(例如,用戶停止了輸入),只被執行一次。這非常適用于,像“搜索框的輸入聯想”這樣的場景。

函數節流:其策略是,“別太快,按我的節奏來”。它會確保一個函數,在一定的時間間隔內(例如,每200毫-秒),最多只被執行一次。這非常適用于,像“頁面滾動”或“窗口縮放”的事件處理。

五、元兇四、內存的“隱形”殺手

最后,一個常常被忽略的、導致界面“間歇性”或“越來越”卡的“隱形殺手”,是不健康的內存使用

內存泄漏:如我們在前文《為什么有些對象在沒有引用后,內存仍無法被回收》中所述,如果程序中,存在“內存泄漏”,那么,隨著用戶使用時長的增加,應用所占用的內存,就會只增不減

與卡頓的關系:當內存占用,達到一個較高的水平時,垃圾回收器,就會被更頻繁地觸發,并且,每一次執行“垃圾回收”所需的時間,也會更長這些垃圾回收的暫停,都是在“主線程”上,進行的“阻塞性”操作。這,正是導致許多應用,“用久了,就越來越卡”的根本原因。

【解決方案】

利用內存分析器:使用像Chrome瀏覽器開發者工具中的“性能”和“內存”面板,來主動地,分析應用的內存使用情況,定位并修復內存泄漏。

養成良好的資源管理習慣:例如,在組件被銷毀時,及時地,注銷所有相關的事件監聽器和定時器。

六、在流程與規范中“防范”

性能作為“一等公民”:在進行需求分析和評審時,就應將“性能”,作為一個重要的非功能性需求,來進行明確的定義和討論。

代碼審查:在進行代碼審查時,應將“是否存在可能阻塞主線程的同步計算”、“高頻事件的處理是否已做節流/防抖”等,作為重要的檢查項。

性能預算:團隊,可以共同制定一個“性能預算”,例如,“我們的任何一個用戶交互,其主線程的響應時間,都不得超過100毫秒”。這個“預算”,可以通過自動化的性能測試工具,在持續集成的流程中,進行監控。

在項目計劃中體現:對于任何一個新功能的開發,都應在項目計劃中,明確地,包含一個用于“性能分析與測試”的任務,以確保,它,不會在緊張的開發節奏中,被遺忘。

常見問答 (FAQ)

Q1: 什么是“主線程”?為什么它這么容易被阻塞?

A1: “主線程”,在圖形用戶界面程序中,是那個唯一的、負責處理所有用戶交互(如點擊、滾動)和執行所有界面更新(如重繪)的線程。正因為它是“唯一”的,所以,任何一個耗時的任務,只要在這個線程上執行,都會“獨占”它,導致它,無法再去處理任何其他的事情,從而,引發界面的“凍結”。

Q2: “函數防抖”和“函數節流”有什么區別?

A2: 兩者都是用于,降低高頻事件回調執行頻率的技術,但策略不同。“防抖”,是在一個連續動作的“結束”時,只執行一次。而“節流”,則是在一個連續動作的“過程”中,以一個固定的頻率(例如,每200毫秒一次),反復地執行。

Q3: 我應該如何使用瀏覽器開發者工具來診斷界面卡頓問題?

A3: 使用“性能”面板。點擊“錄制”按鈕,然后,在你的界面上,復現那個卡頓的操作。錄制結束后,面板,會為你生成一份詳細的“火焰圖”。在這張圖中,那些“頂部平坦”的、耗時很長的、帶有“紅色三角”標記的“長任務”,就是導致你的界面卡頓的“罪魁禍首”。

Q4: 后端程序也會有類似“界面卡頓”的問題嗎?

A4: 后端程序,雖然沒有“界面”,但同樣存在“線程阻塞”的問題。例如,在一個多線程的Web服務器中,如果所有的“工作線程”,都因為一個慢查詢或一個死鎖,而被“阻塞”住了,那么,這個服務器,就將無法再響應任何新的用戶請求,從用戶的角度看,網站,就“卡死”了。其底層的“任務隊列被耗盡”的原理,是相通的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/93801.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/93801.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/93801.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

大規模IP輪換對網站的影響(服務器壓力、風控)

在當下的互聯網環境中,代理IP輪換已經成為爬蟲、SEO、數據采集等行業的常見手段。尤其是大規模數據抓取時,通過代理池實現IP輪換,可以有效避免因單一IP請求過于頻繁而被目標網站封禁。 然而,大規模IP輪換雖然對采集方有利&#xf…

4. STM32 vscode 環境, 官方插件

文章目錄1. 新建配置2. 安裝插件3. 新建工程1. 新建配置 新建vscode 配置, 因為stm32插件比較多, 避免和其他插件沖突。 激活環境: 這里可快速切換: 2. 安裝插件 可選擇安裝最新預覽版: 等待依賴安裝完成后重啟…

【動態規劃:路徑問題】最小路徑和 地下城游戲

最小路徑和(medium) 64. 最小路徑和 ? 給定一個包含非負整數的 m x n 網格 grid ,請找出一條從左上角到右下角的路徑,使得路徑上的數字總和為最小。 ? **說明:**每次只能向下或者向右移動一步。 示例 1&#xff…

SQL詳細語法教程(七)核心優化

以下對 SQL 優化 涉及的關鍵場景(含 update 行鎖優化)進行極致詳細的拆解,從底層原理、執行流程到實戰代碼、避坑指南全維度覆蓋,搭配表格對比讓邏輯更清晰:一、SQL 優化 - COUNT 優化1. 底層原理:COUNT() …

Tomcat 的核心腳本catalina.sh 和 startup.sh的關系

catalina.sh 和 startup.sh 都是 Tomcat 的核心腳本,但它們的角色和使用場景有所不同。以下是它們的主要區別和適用場景:1. 功能區別腳本主要用途底層調用關系startup.sh一個快捷入口腳本,用于快速啟動 Tomcat(后臺模式&#xff0…

飛算JavaAI:簡易貪吃蛇小游戲

目錄先確定核心功能技術選型核心功能實現過程1. 數據模型設計2. 游戲界面和繪制邏輯3. 游戲主框架和事件處理飛算JavaAI在開發中的應用體驗可以進一步優化的地方作為Java課程的小作業,不想做太復雜的管理系統,就選了貪吃蛇這個經典小游戲。全程用Swing做…

如何保障內部網絡安全前提下,實現與外部互聯網之間的文件傳輸?

在數字化時代,企業網絡環境日益復雜,普遍采用“內外網隔離”的安全架構:內部辦公網承載業務系統與數據,外部互聯網則用于對外溝通與信息獲取。這種隔離有效抵御了外部攻擊,但也帶來了“信息孤島”問題——如何在保障內…

計算機視覺 圖片處理 在骨架化過程中,每次迭代都會從圖像的邊緣移除一層像素,直到只剩下單像素寬度的骨架

你說得對,if cv2.countNonZero(binary) 0: break 這個條件確實表示圖像中已經沒有非零像素,即圖像完全變為空白。這并不是骨架化完成的標志,而是表示圖像已經被腐蝕到沒有任何內容了。 在骨架化過程中,我們需要一個更合適的停止條…

rt-thread audio框架移植stm32 adc+dac,用wavplayer錄音和播放

D1 參考 rt-thread官方sdk中,正點原子stm32f429-atk-appollo的board中有audio文件夾,包括了mic/play的程序,wm8978的庫文件因為我們基于stm32h750內置adcdac設計,所以不需要wm8978.c/h。只需要移植drv_sound.c和drv_mic.c D2 工程…

AI重塑軟件測試:質量保障的下一站

軟件開發的世界變化飛快,系統越來越復雜,用戶的胃口越來越大,產品上線的壓力也越來越大。作為測試工程師,你是不是常常覺得傳統測試已經跟不上節奏了?手工測試累死人,自動化腳本維護到崩潰,測試…

【前端基礎知識系列六】React 項目基本框架及常見文件夾作用總結(圖文版)

在 React 開發中,一個清晰合理的項目結構不僅能提高開發效率,還能讓代碼更易于維護和擴展。尤其是在團隊協作中,統一的項目結構規范至關重要。本文將通過圖文結合的方式,詳細介紹 React 項目的基本框架以及常見文件夾的定義與作用…

0815 UDP通信協議TCP并發服務器

Part 1.思維導圖一.UDP通信協議1.原理服務器端:1.用socket函數創建一個套接字文件2.創建服務器端地址結構體并賦值3.用ford函數將套接字文件與地址結構體綁定4.創建接收客戶端地址結構體5.利用sendto和recvfrom函數傳輸和接收信息客戶端:1.用socket函數創…

一個基于純前端技術實現的五子棋游戲,無需后端服務,直接在瀏覽器中運行。

一 功能特性1.1 核心游戲功能- **標準五子棋規則**:1515棋盤,黑子(玩家)先手 - **AI對戰模式**:白子AI具有中等難度,會進行智能進攻和防守 - **勝負判定**:支持橫向、縱向、斜向五子連線獲勝 - **平局檢測**&#xff1…

HBuilderX升級,Vue2 scss 預編譯器默認已由 node-sass 更換為 dart-sass

目錄 一、問題描述 二、問題原因 三、問題解析及解決方案 一、問題描述 最近開發新項目,升級了HBuilderX版本到4.75,最近要在之前的項目添加功能的時候發現報錯,錯誤如下:Vue2 scss 預編譯器默認已由 node-sass 更換為 dart-sa…

像素風球球大作戰 HTML 游戲

像素風球球大作戰 HTML 游戲 下面是一個簡單的像素風格球球大作戰 HTML 游戲代碼&#xff1a; <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-widt…

文件導出時無法獲取響應頭Content-Disposition的文件名

1. 為什么Content-Disposition無法獲取&#xff1f; 要拿到 Content-Disposition 里的 filename&#xff0c;可以用正則或者簡單的字符串解析。 瀏覽器默認不讓前端訪問非標準響應頭&#xff0c;Content-Disposition 需要后端顯式暴露。 在瀏覽器開發者工具 → Network → Re…

Leetcode 128. 最長連續序列 哈希

原題鏈接&#xff1a; Leetcode 128. 最長連續序列 解法1: map&#xff0c;不符合要求 class Solution { public:int longestConsecutive(vector<int>& nums) {if (nums.size()0) return 0;map<int,int> mp;for(auto x: nums){mp[x];}int pre;int l0,r0,res0;…

禾賽激光雷達AT128P/海康相機(2):基于歐幾里德聚類的激光雷達障礙物檢測

目錄 一、參考連接 二、實驗效果?編輯 三、安裝相應的 ros 依賴包 四、代碼驅動 4.1 代碼下載 4.2 代碼文件放置(請按照這個命名放置代碼) 4.3 代碼編譯 4.4 報錯 一、參考連接

Vue Router的常用API有哪些?

文章目錄一、路由配置相關二、路由實例方法&#xff08;router 實例&#xff09;三、組件內路由 API&#xff08;useRouter / useRoute&#xff09;四、導航守衛&#xff08;路由攔截&#xff09;五、路由視圖與導航組件六、其他常用 API七、history模式和hash模式有什么區別&a…

從現場到云端的“通用語”:Kepware 在工業互聯中的角色、使用方法與本土廠商(以胡工科技為例)的差異與優勢

從現場到云端的“通用語”&#xff1a;Kepware 在工業互聯中的角色、使用方法與本土廠商&#xff08;以胡工科技為例&#xff09;的差異與優勢 文章目錄從現場到云端的“通用語”&#xff1a;Kepware 在工業互聯中的角色、使用方法與本土廠商&#xff08;以胡工科技為例&#x…