当前位置: 首页 > article >正文

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;
}

相关文章:

chrome源码中WeakPtr 跨线程使用详解:原理、风险与最佳实践

base::WeakPtr 在 Chromium 中 不能安全地跨线程使用。这是一个很关键的点&#xff0c;下面详细解释原因及正确用法。 &#x1f50d;原理与使用 ✅ 先说答案&#xff1a; base::WeakPtr 本质上是**线程绑定&#xff08;thread-affine&#xff09;**的。不能在多个线程之间创建…...

【Go】从0开始学习Go

文章目录 从0开始学习Go0 与C对比1 代码框架1.1 helloworld式代码示例1.2 主体代码元素&#xff08;核心三部分&#xff09;1.3 其他 2 与C/C区别3 有用的小工具4 注意事项 从0开始学习Go 0 与C对比 特性CGo编译型语言需要编译为机器码直接编译为二进制可执行文件静态类型类型…...

Windows 安装显卡驱动

1.第一步&#xff1a;打开Nvidia 官网驱动下载页面 2.第二步&#xff1a;选择相关信息&#xff0c; 玩游戏选择&#xff0c;GeForce Game Ready ,创意设计、摄影直播 选择 NVIDIA Studio 驱动程序 &#xff08;NVIDIA Studio Driver - WHQL.&#xff09; 2.第三步&#xff1…...

模块与包的导入

一、导入官方库 我们复盘下学习python的逻辑&#xff0c;所谓学习python就是学习python常见的基础语法学习你所处理任务需要用到的第三方库 类别典型库解决的问题学习门槛基础工具os、sys、json操作系统交互、序列化数据&#xff08;如读写 JSON 文件&#xff09;低科学计算n…...

Google设置app-ads.txt

问题&#xff1a; 应用上架后admob后台显示应用广告投放量受限&#xff0c;需要设置app-ads.txt才行。 如何解决&#xff1a; 官方教程: 看了下感觉不难&#xff0c;创建一个txt&#xff0c;将第二条的代码复制进行就得到app-ads.txt了。 然后就是要把这个txt放到哪才可以…...

docker安装rockerMQ

参考Docker部署RocketMQ5.x (单机部署配置参数详解不使用docker-compose直接部署)_rocketmq不推荐用docker部署-CSDN博客 镜像拉取 镜像地址&#xff1a; https://hub.docker.com/r/apache/rocketmq/tags 我在部署的时候最新发行版是5.1.0可以根据需求自行选择一个5.x的版本&a…...

交叉引用、多个参考文献插入、跨文献插入word/wps中之【插入[1-3]、连续文献】

我们在写论文时&#xff0c;需要插入大量参考文献。 有时&#xff0c;一句话需要引用多个文献&#xff0c;如&#xff1a;[1-3]或者[1,3,4]这种形式多个文献插入、跨文献插入。 在上一篇文章中&#xff0c;我们提到可以直接打“-”或者“&#xff0c;”&#xff0c;但是word导出…...

PLC双人舞:profinet转ethernet ip网关奏响施耐德与AB的协奏曲

PLC双人舞&#xff1a;ethernet ip转profinet网关奏响施耐德与AB的协奏曲 案例分析&#xff1a;施耐德PLC与AB PLC的互联互通 在现代工业自动化中&#xff0c;设备之间的互联互通至关重要。本案例旨在展示如何通过北京倍讯科技的EtherNet/IP转Modbus网关&#xff0c;将施耐德P…...

Image and depth from a conventional camera with a coded aperture论文阅读

Image and depth from a conventional camera with a coded aperture 1. 研究目标与实际意义1.1 研究目标1.2 实际问题与产业意义2. 创新方法:编码光圈设计与统计模型2.1 核心思路2.2 关键公式与模型架构2.2.1 图像形成模型2.2.2 深度可区分性准则2.2.3 统计模型与优化框架2.2…...

缺乏团队建设活动,如何增强凝聚力?

当一个团队缺乏系统性的建设活动时&#xff0c;成员之间容易产生疏离感、误解与信任缺失&#xff0c;最终影响整体执行力和目标达成。要有效增强团队凝聚力&#xff0c;应从设计高参与感的团队活动、结合业务与人文目标、营造持续共创的文化机制、推动跨层级协作互动等层面着手…...

特征筛选方法总结

非模型方法 一.FILTER过滤法&#xff1a; 1.缺失值比例&#xff08;80%以上缺失则删除&#xff09;/方差 注意&#xff1a; 连续变量只删方差为0的&#xff0c;因为变量取值范围会影响方差大小。 离散类的看各类取值占比,如果是三分类变量可以视作连续变量。 函数&#xff1a;V…...

力扣HOT100之二叉树:230. 二叉搜索树中第 K 小的元素

这道题直接用最笨的办法来做的&#xff0c;用递归来做&#xff0c;我们定义一个全局变量vector<int> element&#xff0c;然后使用中序遍历&#xff0c;每当碰到一个非空节点就将其加入到向量中&#xff0c;这样依赖当向量中的元素小于k时&#xff0c;就返回0&#xff0c…...

pinia.defineStore is not a function

错误信息表明 pinia.defineStore 不是一个函数,这通常意味着 pinia 没有被正确导入或初始化。 解决方案 检查 Pinia 的导入 确保你从 pinia 中正确导入了 defineStore。正确的导入方式应该是: javascript import { defineStore } from ‘pinia’; 如果你使用的是 createPin…...

入职软件开发与实施工程师了后........

时隔几个月没有创作的我又回来了&#xff0c;这几个月很忙&#xff0c;我一直在找工作&#xff0c;在自考&#xff08;顺便还处理了一下分手的事&#xff09;&#xff0c;到处奔波&#xff0c;心力交瘁。可能我骨子里比较傲吧。我不愿意着急谋生&#xff0c;做我不愿意做的普通…...

PCL点云库点云数据处理入门系列教材目录(2025年5月更新....)

PCL点云库点云数据处理入门系列教材目录 基础阶段 第 1 讲&#xff1a;PCL库简介和安装&#xff08;Win10/11VS2019PCL 1.12.0&#xff09;第 2 讲&#xff1a;PCL库中点云基本知识和数据类型结构第 3 讲&#xff1a;PCL库中点云数据格式PCD和PLY及其输入输出&#xff08;IO&…...

Linux面试题集合(5)

把文件1的内容追加到文件2 cat 文件1>>文件2 把文件1和文件2合并成文件3 cat 文件1 文件2>文件3 使用less查看文件时&#xff0c;搜寻ab字符 /ab 用more和less如何查看文件 more&#xff1a; CtrlF -- 向下滚动一屏 CtrlB -- 返回上一屏 f -- 向下翻屏 b -- 向上翻屏 …...

python动漫论坛管理系统

目录 技术栈介绍具体实现截图系统设计研究方法&#xff1a;设计步骤设计流程核心代码部分展示研究方法详细视频演示试验方案论文大纲源码获取/详细视频演示 技术栈介绍 Django-SpringBoot-php-Node.js-flask 本课题的研究方法和研究步骤基本合理&#xff0c;难度适中&#xf…...

【ubuntu24.04】pycharm 死机结束进程

windows 远程pycharm到ubuntu执行程序 pycharm 在调试过程中&#xff0c;内存耗尽&#xff0c;然后死机了 pycharm 进程 (base) rootk8s-master-pfsrv:/home/zhangbin/下载# ps -ef | grep pycharm root 121245 3230568 0 5月14 pts/8 00:00:00 /bin/bash --rcfile …...

Java 中Supplier延迟生成值的原因

在编程中&#xff0c;延迟生成值&#xff08;Lazy Value Generation&#xff09; 是指将值的计算或生成过程推迟到真正需要使用该值时才执行。这一机制的核心是避免不必要的计算&#xff0c;提升程序的性能和资源利用率。结合 Supplier 和 Optional 的使用场景&#xff0c;我们…...

设置windows10同时多用户登录方法

RDP wrapper 的版本更新停止在2017年&#xff0c; 找到网上其它大神更新的软件&#xff0c; 参考&#xff1a;RDPWrap v1.8.9.9 (Windows家庭版开启远程桌面、Server解除远程数量限制&#xff09; - 吾爱破解 - 52pojie.cn 我的需求是在离线环境中布置&#xff0c;方法是&…...

Web 技术与 Nginx 网站环境部署

这里写目录标题 一. Web基础域名和DNS域名的概念域名的结构域名结构类型 Hosts文件Hosts文件的作用修改Hosts文件 DNS域名注册 网页与HTML网页概述HTML概述HTML基本标签HTML语法规则HTML文件结构 网站和主页Web1.0 与 Web2.0 静态网页与动态网页静态网页动态网页动态网页语言 H…...

分组背包问题:如何最大化背包价值?

有 N 组物品和一个容量是 V 的背包。 每组物品有若干个&#xff0c;同一组内的物品最多只能选一个。 每件物品的体积是 vij &#xff0c;价值是 wij &#xff0c;其中 i 是组号&#xff0c;j 是组内编号。 求解将哪些物品装入背包&#xff0c;可使物品总体积不超过背包容量&…...

nodejs快速入门到精通1

参考 nodejs快速入门到精通 菜鸟教程-nodejs nodejs官方文档 原因 视频免费 资料收费 笔记还是自己写吧 安装 nodejs官网 windows下&#xff1a; #查看nodejs版本 node -v #查看npm版本 npm -v #设置npm为淘宝镜像源 npm config set registry https://registry.npmmirror.…...

FP8精度革命:Hopper架构下大模型训练的误差传播控制方法

点击 “AladdinEdu&#xff0c;同学们用得起的【H卡】算力平台”&#xff0c;H卡级别算力&#xff0c;按量计费&#xff0c;灵活弹性&#xff0c;顶级配置&#xff0c;学生专属优惠。 一、FP8为何成为大模型训练的新范式&#xff1f; 1.1 算力需求与精度演进的矛盾 当前大语言…...

手动制做一个Transformer

本文来自I made a transformer by hand . 一直以来&#xff0c;笔者对 transformer 的注意力机制、qkv的理解都浮于表面&#xff0c;当然也不是说我看完 I made a transformer by hand 后理解有多深入&#xff0c;但确实加深了我对相关概念的理解&#xff0c;故搬运此文章&…...

已解决——如何让网站实现HTTPS访问?

一、申请SSL证书 SSL证书是HTTPS实现的关键&#xff0c;它由受信任的证书颁发机构&#xff08;CA&#xff09;签发&#xff0c;用于验证网站的身份并加密数据传输。以下是申请SSL证书的常见步骤&#xff1a; 选择证书类型 根据网站的需求和预算&#xff0c;选择合适的SSL证书…...

WebRTC技术EasyRTC嵌入式音视频通信SDK助力智能电视搭建沉浸式实时音视频交互

一、方案概述​ EasyRTC是一款基于WebRTC技术的开源实时音视频通信解决方案&#xff0c;具备低延迟、高画质、跨平台等优势。将EasyRTC功能应用于智能电视&#xff0c;能够为用户带来全新的交互体验&#xff0c;满足智能电视在家庭娱乐、远程教育、远程办公、远程医疗等多种场…...

Unreal Engine: Windows 下打包 AirSim项目 为 Linux 平台项目

环境&#xff1a; Windows: win10, UE4.27, Visual Studio 2022 Community.Linux: 22.04 windows环境安装教程&#xff1a; 链接遇到的问题&#xff08;问题&#xff1a;解决方案&#xff09; 点击Linux打包按钮&#xff0c;跳转至网页而不是执行打包流程&#xff1a;用VS打开项…...

Spring MVC HttpMessageConverter 的作用是什么?

HttpMessageConverter (HTTP 消息转换器) 是 Spring MVC 框架中一个非常核心的组件&#xff0c;它的主要作用是在 HTTP 请求、响应体与 Java 对象之间进行双向转换。 核心作用&#xff1a; 读取请求体 (Request Body) 到 Java 对象&#xff1a; 当 Controller 方法的参数使用 …...

小乌龟git中的推送账户、作者账户信息修改

文章目录 修改git文档作者信息修改git推送用户信息参考文献 修改git文档作者信息 小乌龟中的用户信息为&#xff1a;作者信息&#xff0c;并非推送用户。 上边用户信息&#xff0c;修改的是文件的作者信息。如果想要修改git服务中记录的推送用户信息需要修改推送用户信息。 …...