chrome源碼中WeakPtr 跨線程使用詳解:原理、風險與最佳實踐

????????base::WeakPtr 在 Chromium 中 不能安全地跨線程使用。這是一個很關鍵的點,下面詳細解釋原因及正確用法。

🔍原理與使用

? 先說答案:

  • base::WeakPtr 本質上是**線程綁定(thread-affine)**的。
  • 不能在多個線程之間創建或使用它,特別是不能在一個線程創建 WeakPtr,然后在另一個線程中使用它。
  • 如果你非要跨線程判斷對象是否有效,應使用更底層或線程安全的機制(例如 RefCountedThreadSafe + scoped_refptr)。

📌 官方說明

Chromium 的 base::WeakPtr 文檔中有明確說明(可在源碼注釋中看到):

WeakPtr is not thread-safe. It must be used and dereferenced on the same thread (or Sequence) on which it was obtained from a WeakPtrFactory.

這段代碼是chrome源碼里面的WeakPtr實現:

WeakPtr<T> GetWeakPtr() { return WeakPtr<T>(weak_reference_owner_.GetRef(), reinterpret_cast<T*>(ptr_)); }

是否線程安全?答案是: ? 不是線程安全的,除非你明確知道 weak_reference_owner_ 是線程安全的,而且 ptr_ 的生命周期也受線程安全保護(比如引用計數等)。


🔍 逐行分析:

1. weak_reference_owner_.GetRef()

這看起來是一個類似 base::internal::WeakReferenceOwner 或你自定義的 WeakReferenceOwner 類型。Chromium 中的 WeakReferenceOwner 默認是 線程綁定的(Thread-affine),只能在創建它的線程中使用(否則在 Debug 模式下會崩)。

? 所以:GetRef() 調用必須在原始線程中,不是線程安全的。


2. reinterpret_cast<T*>(ptr_)

這個操作沒做線程安全保護的話,也是不安全的,尤其是如果 ptr_ 不是線程安全引用類型(如 scoped_refptr<T>)。


? 正確的 WeakPtr 創建姿勢(來自 Chromium)

Chromium 推薦用 base::WeakPtrFactory<T>,并且只能在綁定線程上使用它:

class MyClass { public: base::WeakPtr<MyClass> GetWeakPtr() { return weak_factory_.GetWeakPtr(); // ? 安全:只能在同一個線程上用 } private: base::WeakPtrFactory<MyClass> weak_factory_{this}; };

  • WeakPtrFactory<T> 本身有一個 SequenceChecker,確保你只能在創建線程中用 GetWeakPtr()

  • 即使你多線程中有多個任務,都只能在同一個線程中訪問和使用這個 WeakPtrFactory


? 錯誤使用示例

如果你把 GetWeakPtr() 這個函數暴露給了多個線程,并且多個線程中調用了它,比如這樣:

thread1: my_object->GetWeakPtr(); thread2: my_object->GetWeakPtr();

即便 GetWeakPtr() 本身沒有 crash,也會存在競爭風險或觸發 DCHECK。


? 如果你真的想跨線程構建 WeakPtr...

你可以:

  1. 在初始化線程上調用 GetWeakPtr()

  2. 然后把這個 WeakPtr 通過 PostTask 發送到其他線程使用(讀取、檢查是否有效)。

  3. 不能在其他線程上繼續調用 GetWeakPtr()


? 總結表格

行為是否線程安全建議
同一線程調用 GetWeakPtr()? 安全? 推薦
不同線程調用 GetWeakPtr()? 不安全? 禁止
一個線程創建 WeakPtr,另一個線程只用它?? 有條件安全?? 小心使用
自己實現 GetWeakPtr() 如你代碼中那樣? 不建議? 風險高


如你有多線程需求,建議考慮:

  • 使用 scoped_refptr<T>(跨線程完全安全);

  • 或將 WeakPtrFactory 遷移到后臺線程中,并保證所有訪問都在同一個線程;

  • 或用 CrossThreadWeakPtr 模式(Chromium中個別模塊使用,較復雜)。

這段代碼的作用是 在回調或異步任務執行時,確保 weak_self 所指向的對象仍然有效(未被銷毀),以防止訪問已經釋放的對象從而導致 野指針或崩潰


🔍 代碼解析:

if (!weak_self || weak_self.WasInvalidated()) { return; }

這是一個典型的 base::WeakPtr<T> 使用場景,常用于異步任務或跨線程操作中。

? 判斷目的:
  1. !weak_self

    • 檢查 weak_self 是否為空(即未綁定任何對象)。

    • 這相當于檢查 WeakPtr 是否有效初始化。

  2. weak_self.WasInvalidated()

    • 判斷該 WeakPtr 是否已經 失效(即指向的原始對象已經銷毀)

    • 即使指針本身不為空,如果 T 已被析構,也不能再訪問。


🛡? 為什么這么寫?

異步/延遲任務常見問題:

舉個例子:

// 假設這是某個類中的成員函數 PostTask(FROM_HERE, base::BindOnce(&MyClass::DoWork, weak_factory_.GetWeakPtr()));

  • 如果 MyClass 在任務執行前被析構,那么 DoWork() 中的 this 就是懸空指針。

  • WeakPtr 就是防止“任務晚于對象生命周期”這種典型 bug。

  • 所以在 DoWork() 內部首先要判斷 weak_self 是否有效。


🧠 小結:

if (!weak_self || weak_self.WasInvalidated()) { return; // 安全退出,防止訪問已銷毀對象 }

這個判斷非常關鍵,用于:

  • 保證異步回調安全性;

  • 防止 use-after-free;

  • 是 C++ 客戶端編程(尤其是 Chromium 項目)中的推薦模式。

🛡? 工程案例:

cookie_manager->SetCanonicalCookie(*canonical_cookie, cookie_info.url, options, base::BindOnce(&InProcessImporterBridge::SetCanonicalCookieCallback, weak_factory_.GetWeakPtr())); 這個代碼在ui線程,SetCanonicalCookieCallback這個回調要是在其他線程調用會有什么問題\

在 Chromium 的線程模型中,SetCanonicalCookieCallback?回調若在其他線程(非 UI 線程)調用可能導致嚴重問題。以下是詳細分析:


問題根源

  1. 線程安全性違例

    • InProcessImporterBridge?類及其成員變量通常非線程安全,假設僅在 UI 線程訪問。

    • 若回調在其他線程(如 IO 線程)執行,直接訪問成員變量會導致數據競爭(Data Race)。

  2. weak_factory_.GetWeakPtr()?的線程安全性

    • WeakPtrFactory?本身線程安全(GetWeakPtr()?可跨線程調用),但不保證回調執行時的對象存活

    • 若 UI 線程已銷毀?InProcessImporterBridge,其他線程調用回調會訪問無效內存。

  3. UI 線程依賴的操作

    • 回調中若操作 UI 相關資源(如更新界面、訪問?Profile),必須在 UI 線程執行。


具體風險場景

// 錯誤示例:回調可能在其他線程執行
cookie_manager->SetCanonicalCookie(*canonical_cookie, cookie_info.url, options,base::BindOnce(&InProcessImporterBridge::SetCanonicalCookieCallback,weak_factory_.GetWeakPtr()));  // 若回調在非UI線程運行
  • 崩潰風險

    • 其他線程調用時,若?InProcessImporterBridge?已銷毀,weak_factory_?返回?nullptr,回調被丟棄(安全)。

    • 但若對象存活,回調直接訪問成員變量(如?std::vectorstd::map)會觸發未定義行為。

  • 邏輯錯誤

    • 非 UI 線程修改 UI 相關狀態(如界面標志位),導致界面顯示異常。


解決方案

1.?強制回調到 UI 線程

使用?base::BindPostTask?將回調派發到 UI 線程的任務隊列:

cookie_manager->SetCanonicalCookie(*canonical_cookie,cookie_info.url,options,base::BindOnce(base::BindPostTask(base::SequencedTaskRunner::GetCurrentDefault(),  // 獲取UI線程TaskRunnerbase::BindOnce(&InProcessImporterBridge::SetCanonicalCookieCallback,weak_factory_.GetWeakPtr())))
);
2.?回調中顯式檢查線程

在回調內部驗證是否運行在 UI 線程:

void InProcessImporterBridge::SetCanonicalCookieCallback(...) {DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);// 安全操作成員變量
}
3.?使用?ThreadSafeRefCounted(若適合)

若?InProcessImporterBridge?需跨線程共享,改為繼承?base::RefCountedThreadSafe,但需確保所有成員訪問均線程安全(通常不推薦用于 UI 相關類)。


為什么?WeakPtr?不能完全解決問題?

  • WeakPtr?僅保證對象銷毀后不執行回調,但若對象存活:

    • 不保證回調線程安全。

    • 不自動切換線程上下文。

  • 必須配合?BindPostTask?或線程檢查使用。


Chromium 的線程模型要求

  1. UI 線程專屬對象

    • 如?ProfileWebContents、UI 控件等,必須通過 UI 線程訪問。

  2. 線程跳轉代價

    • 跨線程任務提交(如?PostTask)有開銷,但比崩潰/數據競爭更可接受。


正確代碼示例

// 方法1:使用 BindPostTask(推薦)
cookie_manager->SetCanonicalCookie(*canonical_cookie,cookie_info.url,options,base::BindPostTask(base::SequencedTaskRunner::GetCurrentDefault(),base::BindOnce(&InProcessImporterBridge::SetCanonicalCookieCallback,weak_factory_.GetWeakPtr()))
);// 方法2:回調內手動跳轉(備用)
class InProcessImporterBridge {public:void SetCanonicalCookieCallback(...) {if (!base::SequencedTaskRunner::GetCurrentDefault()->RunsTasksInCurrentSequence()) {base::SequencedTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,base::BindOnce(&InProcessImporterBridge::SetCanonicalCookieCallback,weak_factory_.GetWeakPtr(), ...));return;}DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);// 實際處理邏輯}
};

總結

問題風險等級解決方案
非UI線程訪問成員變量高(崩潰)用?BindPostTask?跳轉回 UI 線程
WeakPtr?對象已銷毀低(安全)自動跳過回調
未同步的 UI 狀態修改中(邏輯錯誤)添加?DCHECK_CALLED_ON_VALID_SEQUENCE

最佳實踐

  • 默認假設回調可能在任何線程執行。

  • 對 UI 相關對象,始終通過?BindPostTask?或?PostTask?確保線程安全。

下面是個跨線程場景使用的一個完整例子

class QQBrowserImporter : public Importer {
public:QQBrowserImporter();explicit QQBrowserImporter(bool first_run);void StartImport(const importer::SourceProfile& source_profile,uint16_t items,ImporterBridge* bridge) override;static void ImportCookies(const std::wstring& localPath, base::WeakPtr<QQBrowserImporter> client);protected:friend class base::RefCountedThreadSafe<QQBrowserImporter>;~QQBrowserImporter() override;private:scoped_refptr<base::SequencedTaskRunner> GetdbTaskRunner();bool ScheduleTask(const base::RepeatingClosure& task);scoped_refptr<base::SequencedTaskRunner> db_thread_runner_;base::WeakPtrFactory<QQBrowserImporter> weak_factory_;DISALLOW_COPY_AND_ASSIGN(QQBrowserImporter);
};QQBrowserImporter::QQBrowserImporter(bool first_run) : first_run_(first_run), weak_factory_(this) {}QQBrowserImporter::~QQBrowserImporter(void){}void QQBrowserImporter::StartImport(const importer::SourceProfile& source_profile,uint16_t items,ImporterBridge* bridge) {bridge_ = bridge;bridge_->NotifyStarted();std::wstring source_path = source_profile.source_path.value();if ((items & importer::COOKIES) && !cancelled()) {ScheduleTask(base::BindRepeating(&QQBrowserImporter::ImportCookies, source_path, weak_factory_.GetWeakPtr()));}
}// TODO extract to base class
void QQBrowserImporter::ImportCookies(const std::wstring& localPath, base::WeakPtr<QQBrowserImporter> client) {std::wstring local_data_path = localPath;std::string desc;local_data_path += L"\\Default\\Network\\Cookies";base::FilePath qq_login_path(local_data_path);sql::Database db;if (!db.Open(qq_login_path)) {content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,base::BindOnce([](base::WeakPtr<QQBrowserImporter> weak_self) {if (!weak_self || weak_self.WasInvalidated()) {return;}if (weak_self->bridge_) {base::Value::Dict import_res;import_res.Set("successcount", 0);import_res.Set("faileddesc", "Failed to open QQ login data DB");std::string value_str;base::JSONWriter::Write(import_res, &value_str);weak_self->bridge_->NotifyEnded(false, importer::TYPE_COOKIES_QQ, value_str);}},client));return;}const char* kQuery = "SELECT host_key, encrypted_value, name, path, creation_utc, expires_utc, last_access_utc, is_secure, is_httponly, samesite, priority FROM cookies";sql::Statement stmt(db.GetUniqueStatement(kQuery));if (!stmt.is_valid()) {content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,base::BindOnce([](base::WeakPtr<QQBrowserImporter> weak_self) {if (!weak_self || weak_self.WasInvalidated()) {return;}if (weak_self->bridge_) {base::Value::Dict import_res;import_res.Set("successcount", 0);import_res.Set("faileddesc", "Failed to prepare login query statement");std::string value_str;base::JSONWriter::Write(import_res, &value_str);weak_self->bridge_->NotifyEnded(false, importer::TYPE_COOKIES_QQ, value_str);}},client));return;}std::string localPathUtf8 = base::WideToUTF8(localPath);auto aesKey = chrome::GetDecryptedKey(localPathUtf8);std::vector<importer::CookiesInfo> cookies_info;while (stmt.Step()) {std::string host_key  = stmt.ColumnString(0);std::string name = stmt.ColumnString(2);std::string path = stmt.ColumnString(3);int64_t creation_utc = stmt.ColumnInt64(4);int64_t expires_utc = stmt.ColumnInt64(5);int64_t last_access_utc = stmt.ColumnInt64(6);// compatible with old browser kernels, new kernels will add SameSite=None by defaultint is_http_only = true; // stmt.ColumnInt(8);int is_secure = true; // stmt.ColumnInt(7);int samesite_val = stmt.ColumnInt(9);int priority_val = stmt.ColumnInt(10);base::span<const uint8_t> blob_span = stmt.ColumnBlob(1);std::vector<unsigned char> encrypted(blob_span.begin(), blob_span.end());std::string value;if (encrypted.size() > 15 && encrypted[0] == 'v' && encrypted[1] == '1') {std::vector<unsigned char> nonce(encrypted.begin() + 3, encrypted.begin() + 15);std::vector<unsigned char> ciphertext(encrypted.begin() + 15, encrypted.end() - 16);std::vector<unsigned char> tag(encrypted.end() - 16, encrypted.end());auto decrypted = chrome::AESGCMDecrypt(aesKey, nonce, ciphertext, tag);value = std::string(decrypted.begin(), decrypted.end());}else {continue;}std::string cookie_str = FormatCookieString(name, value, host_key, path, expires_utc, is_secure == 0 ? false : true, is_http_only == 0 ? false : true, samesite_val);std::string scheme = is_secure ? "https://" : "http://";GURL url(scheme + ConvertHostKeyToDomain(host_key) + path);cookies_info.emplace_back(std::move(cookie_str), std::move(url), is_http_only);}if (cookies_info.empty()) {content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,base::BindOnce([](base::WeakPtr<QQBrowserImporter> weak_self) {if (!weak_self || weak_self.WasInvalidated()) {return;}if (weak_self->bridge_) {base::Value::Dict import_res;import_res.Set("successcount", 0);import_res.Set("faileddesc", se_import_user_info::kEmptyData);std::string value_str;base::JSONWriter::Write(import_res, &value_str);weak_self->bridge_->NotifyEnded(false, importer::TYPE_COOKIES_QQ, value_str);}},client));return;}auto task = base::BindOnce([](base::WeakPtr<QQBrowserImporter> weak_self, std::vector<importer::CookiesInfo> cookies_info) {if (!weak_self || weak_self.WasInvalidated()) {return;}if (weak_self->bridge_) {weak_self->bridge_->SetCookie(cookies_info, se_import_user_info::BrowserType::kQQ, importer::TYPE_COOKIES_QQ);}}, client, cookies_info);content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(task));
}scoped_refptr<base::SequencedTaskRunner> QQBrowserImporter::GetdbTaskRunner() {if (!db_thread_runner_) {db_thread_runner_ = base::ThreadPool::CreateSingleThreadTaskRunner({base::TaskPriority::HIGHEST, base::MayBlock()});}return db_thread_runner_;
}bool QQBrowserImporter::ScheduleTask(const base::RepeatingClosure& task) {scoped_refptr<base::SequencedTaskRunner> task_runner(GetdbTaskRunner());if (task_runner.get())return task_runner->PostTask(FROM_HERE, task);return false;
}

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

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

相關文章

hysAnalyser 從MPEG-TS導出ES功能說明

摘要 hysAnalyser 是一款特色的 MPEG-TS 數據分析工具。本文主要介紹了 hysAnalyser 從MPEG-TS 中導出選定的 ES 或 PES 功能(版本v1.0.003)&#xff0c;以便用戶知悉和掌握這些功能&#xff0c;幫助分析和解決各種遇到ES或PES相關的實際問題。hysAnalyser 支持主流的MP1/MP2/…

C++(21):fstream的讀取和寫入

目錄 1 ios::out 2 ios::in和is_open 3 put()方法 4 get()方法 4.1 讀取單個字符 4.2 讀取多個字符 4.3 設置終結符 5 getline() 1 ios::out 打開文件用于寫入數據。如果文件不存在&#xff0c;則新建該文件&#xff1b;如果文件原來就存在&#xff0c;則打開時清除…

系統架構設計(十七):微服務數據一致性和高可用策略

數據一致性問題 問題本質 由于每個微服務擁有獨立數據庫&#xff0c;跨服務操作不能用傳統的數據庫事務&#xff0c;面臨“分布式事務”一致性挑戰。 數據一致性策略 策略核心思想應用場景優缺點強一致性&#xff08;Strong Consistency&#xff09;所有操作實時同步成功&a…

os agent智能體軟件 - 第三彈 - 純語音交互

前兩期期我們發布了產品的初級形態&#xff0c;那時候還只能是“軟件開發者”在本地配置使用&#xff0c;或者運行起來有個大黑框&#xff0c;使用起來美觀度太差。 到今天大概20天&#xff0c;我們的第3版已經出來了&#xff0c;不僅做成了電腦端的exe軟件&#xff08;任何人…

鏈表原理與實現:從單鏈表到LinkedList

1.鏈表的概念及結構 鏈表是一種物理存儲結構上非連續存儲結構&#xff0c;數據元素的邏輯順序是通過鏈表中的引用鏈接次序實現的 。 可以形象的理解&#xff0c;在邏輯上來看&#xff0c;鏈表就像是一節節火車車廂。 鏈表的分類&#xff1a;鏈表的結構有很多種&#xff0c;單向…

替換word中的excel

PostMapping("/make/report/target/performance/first") public AjaxResult makeTargetReportFirst(RequestBody MakeReportDTO makeReportDTO) {Map<String, String> textReplaceMap new HashMap<>();// 替換日期LocalDateTime nowData LocalDateTime…

深入探索百度智能云千帆AppBuilder:從零開始構建AI應用

在數字化轉型的浪潮中&#xff0c;企業對高效、智能的應用開發平臺的需求日益增長。百度智能云千帆AppBuilder&#xff08;以下簡稱AppBuilder&#xff09;憑借其強大的功能和靈活的開發方式&#xff0c;成為企業級大模型應用開發的理想選擇。本文將詳細介紹如何使用AppBuilder…

測試工程師要如何開展單元測試

單元測試是軟件開發過程中至關重要的環節&#xff0c;它通過驗證代碼的最小可測試單元(如函數、方法或類)是否按預期工作&#xff0c;幫助開發團隊在早期發現和修復缺陷&#xff0c;提升代碼質量和可維護性。以下是測試工程師開展單元測試的詳細步驟和方法&#xff1a; 一、理…

NODE-I916 I721模塊化電腦發布,AI算力與超低功耗的完美平衡

在智能工業與邊緣計算蓬勃發展的今天&#xff0c;企業對計算設備的性能與能效需求日益嚴苛。全新推出NODE-I916與NODE-I721模塊化電腦&#xff0c;分別搭載英特爾 酷睿? Ultra 平臺與Alder Lake-N平臺&#xff0c;以差異化CPU配置為核心&#xff0c;為AI推理、工業自動化及嵌入…

采集需要登錄網站的教程

有些網站需要用戶登錄才能顯示相關信息&#xff0c;如果要采集這類網站&#xff0c;有以下幾個方法&#xff1a; 1. 寫發布模塊來抓包獲取post的數據&#xff1b; 2. 有些采集器內置瀏覽器獲取這些信息&#xff0c;但是經常獲取的不準確&#xff0c;可靠性太低&#xff1b; 3. …

六足連桿爬行機器人的simulink建模與仿真

目錄 1.課題概述 2.系統仿真結果 3.核心程序 4.系統原理簡介 5.完整工程文件 1.課題概述 六足連桿爬行機器人的simulink建模與仿真。通過simulink&#xff0c;對六足機器人的六足以及機身進行simulink建模&#xff0c;模擬其行走&#xff0c;仿真輸出機器人行走時六足的坐…

什么是物聯網 (IoT):2024 年物聯網概述

物聯網&#xff08;IoT&#xff09;是一個有望徹底改變我們生活、工作以及與環境互動方式的概念。如今&#xff0c;越來越多的新興企業和老牌企業都在利用物聯網的力量創造創新產品與服務。正因為這一轉變&#xff0c;互聯互通已成為我們生活中不可或缺的一部分&#xff0c;科技…

MVC入門(5)-- HttpMessageConverter 消息轉換器

概念 HttpMessageConverter 是 Spring 框架中用于處理 HTTP 請求和響應數據的核心接口&#xff0c;負責在 Java 對象與 HTTP 消息體&#xff08;請求體或響應體&#xff09;之間進行雙向轉換。簡單來說&#xff0c;它是 Spring 用來將 HTTP 請求中的原始數據&#xff08;如 JS…

Spark,連接MySQL數據庫,添加數據,讀取數據

以下是使用Spark連接MySQL數據庫、添加數據和讀取數據的步驟&#xff08;基于Scala API&#xff09;&#xff1a; 1. 準備工作 - 添加MySQL驅動依賴 在Spark項目中引入MySQL Connector JAR包&#xff08;如 mysql-connector-java-8.0.33.jar &#xff09;&#xff0c;或通過Sp…

關于 APK 反編譯與重構工具集

一、apktool — APK 解包 / 重打包 apktool 是一款開源的 Android APK 工具&#xff0c;用于&#xff1a; 反編譯 APK 查看資源和布局文件 生成 smali 文件&#xff08;DEX 的反匯編&#xff09; 對 APK 進行修改后重新打包 它不能還原 Java 源碼&#xff0c;只能將 D…

[解決方案] Word轉PDF

背景&#xff1a; 之前做過一些pdf導出&#xff0c; 客戶提了一個特別急的需求&#xff0c; 要求根據一個模版跟一個csv的數據源&#xff0c; 批量生成PDF&#xff0c; 因為之前用過FOP&#xff0c; 知道調整樣式需要特別長的時間&#xff0c; 這個需求又特別急&#xff0c; 所…

01 基本介紹及Pod基礎

01 查看各種資源 01-1 查看K8s集群的內置資源 [rootmaster01 ~]# kubectl api-resources NAME SHORTNAMES APIVERSION NAMESPACED KIND bindings v1 …

19 C 語言位運算、賦值、條件、逗號運算符詳解:涵蓋運算符優先級與復雜表達式計算過程分析

1 位運算符 位運算符是對整數的二進制表示&#xff08;補碼形式&#xff09;進行逐位操作的運算符。以下是主要的位運算符及其功能描述&#xff1a; 運算符描述操作數個數副作用&按位與2無|按位或2無^按位異或2無~按位取反1無<<按位左移2無>>按位右移2無 1.1…

哈希查找方法

已知哈希表長度為11&#xff0c;哈希函數為H&#xff08;key&#xff09;&#xff1d;key&#xff05;11&#xff0c;隨機產生待散列的小于50的8個元素&#xff0c;同時采用線性探測再散列的方法處理沖突。任意輸入要查找的數據&#xff0c;無論是否找到均給出提示信息。 int f…

JavaScript性能優化實戰(10):前端框架性能優化深度解析

引言 React、Vue、Angular等框架雖然提供了強大的抽象和開發效率,但不恰當的使用方式會導致嚴重的性能問題,針對這些問題,本文將深入探討前端框架性能優化的核心技術和最佳實踐。 React性能優化核心技術 React通過虛擬DOM和高效的渲染機制提供了出色的性能,但當應用規模…