std::promise彻底讲透:多线程结果传递与异常处理的实战指南

std::promise 这个词,很多同学在简历里写过“熟悉多线程编程”,但真到面试官追问起来,往往只能背出“用来配合 future 获取线程结果”这一句。再往下问一问内部机制、异常传递、析构行为,就支支吾吾了。这不能怪大家,网上讲 promise 的文章大多是 API 介绍,真正把它放到多线程通信体系里讲透、把面试题背后的考察点拆清楚的太少。这篇就换个角度,从原理、实战、面试三个维度,把 std::promise 一次讲透。适合正在准备后端、客户端岗位面试的朋友,也适合项目里需要做线程间结果传递、但又怕用错的开发者。

1. std::promise 到底是什么?先把它拆成两半理解

1.1 promise 和 future 是一对“承诺—兑现”票据

一句话直白版:std::promise 是一个“承诺方”,它承诺将来某个时刻会给出一个结果;std::future 是“索取方”,它手里拿着凭据,等结果准备好了就去取。

这样说有点抽象,换个场景:你在一家餐馆点了外卖,商家给你一张取餐号。你不需要一直站在柜台前盯着出餐口,而是可以找个位置坐下玩手机。商家出餐后会喊号,你听到号再去端。这里,std::promise 就相当于商家内部那个“会出餐的后厨备餐流程”,而 std::future 就是那张取餐号。

代码层面更直白:

std::promise<int> prom; std::future<int> fut = prom.get_future(); // 在某个线程里做计算,最终把结果交给 prom std::thread t([&prom] { int result = 42; prom.set_value(result); // 出餐完成,喊号 }); // 主线程不阻塞地等待 int answer = fut.get(); // 听到号,取餐 t.join();

线程之间通过 promise/future 这对组合完成了一次“值传递”。注意这里不需要你手动加锁,也不需要条件变量,promise 内部已经帮你处理好了同步关系。

1.2 promise 的三种结局:设值、设异常、或者“失约”

一个 promise 对象从创建到销毁,最终只会面临三种情况:

第一,调用set_value(),把正常结果送进共享状态。这是最常见的情况,future.get() 取到这个正常值。

第二,调用set_exception(),把异常对象送进共享状态。调用方 future.get() 时会直接把这个异常重新抛出来。

第三,promise 析构了,但什么都没设置。这等价于“承诺方没出餐就关门了”,future.get() 会抛出一个std::future_error,错误码通常是broken_promise,翻译过来就是“承诺被打破了”。

理解这第三种情况非常关键。面试官经常拿它出题:“如果一个 promise 在没设置结果前就被析构了,future 会怎样?”答案是 future 会抛异常,而不是永远阻塞。这个设计其实很巧妙——如果 promise 消失会导致 future 无限阻塞,程序就永久卡死了。抛出 broken_promise 至少给了调用方一个“出问题了”的信号。

2. 为什么有条件变量了,还要 std::promise?

2.1 条件变量解决“持续共享”问题,promise 专注“一次性传递”

我见过不少初学者疑惑:线程间要传数据,用条件变量加一个标志位不也能实现吗?为什么非要用 promise?

先看条件变量的经典写法:

std::mutex mtx; std::condition_variable cv; bool ready = false; int result; // 工作线程 void worker() { // 模拟计算 result = 42; { std::lock_guard<std::mutex> lk(mtx); ready = true; } cv.notify_one(); } // 主线程 std::unique_lock<std::mutex> lk(mtx); cv.wait(lk, [] { return ready; }); int val = result; // 注意还需要持有锁才能读

这段代码能跑,但它把“一次性的结果通知”用“可变的共享状态”来实现,引入了锁、条件变量、标志位、谓词四个概念。而且条件变量本身是用来解决“多个线程持续等待某个条件成立”的场景,比如生产者消费者队列里队列非空,这种条件会反复出现。如果只是为了传一次结果,条件变量属于大材小用。

promise 的定位非常明确:它就是“一次性”的通道。每个 promise 只能关联一个 future,结果只能被设置一次,future 只能被消费一次(get 只能调用一次)。它的内部状态从 pending 到 ready,走完就走完了,没有回头路。这种“一次性”语义让代码更简洁,也让心智负担小很多。

2.2 它和 std::async、std::packaged_task 又是什么关系?

很多人学了 async 就不太用 promise,这正常。async 确实是一站式方案:

std::future<int> fut = std::async(std::launch::async, [] { return 42; }); int val = fut.get();

但这不等于 promise 没用了。async 是把“任务创建 + 线程调度 + 结果传递”全部打包的高层工具,它内部也离不开 promise。而你在以下场景中会发现直接使用 promise 反而更顺手:

  • 你需要自己管理线程池,任务队列中的每个任务需要把结果送回提交者;
  • 你需要把“设置结果”这个动作和“启动线程”拆开,比如任务在事件循环某个回调里完成,这时候异步操作的触发点并不一定在线程创建处;
  • 你需要把同一个结果广播给多个消费者,这时候会用到 promise 加 shared_future。

std::packaged_task 则是另一个角度的封装:它把一个可调用对象和一个 promise 绑定在一起,调用 task 就会执行函数并自动把返回值塞进 promise。然后你可以通过 task.get_future() 拿到 future。如果函数抛出异常,promise 也会自动保存异常。可以说 packaged_task 是“想把任务丢进线程池,又不想手写 set_value”时的更好选择。而 promise 更底层、更自由,你可以在任意时刻、任意线程里手动调用 set_value,不需要它在某个函数返回时自动触发。

所以三者的关系可以这样记:promise 是“手动挡”,最灵活;packaged_task 是“半自动挡”,帮你绑定了可调用对象;async 是“全自动挡”,连线程调度都包办了。面试时能把这个关系讲清楚,通常能给到不错的印象分。

3. 手写一个能跑的实例:把耗时计算结果送回调用线程

3.1 场景设计:模拟一个耗时子任务

假设一个文件解析工具,有一个主线程需要读取配置文件,但解析工作比较耗时,希望提交给一个独立线程执行,主线程先做别的准备工作,最后再等解析结果。

这个场景就很适合 promise:提交方和计算方分离,结果是“一次性”的,而且解析过程中可能遇到格式错误,需要把异常也传回来。

3.2 完整代码:正常值传递与异常传递

来看一套完整的实现:

#include <iostream> #include <future> #include <thread> #include <chrono> #include <string> #include <stdexcept> std::string parse_file(const std::string& path, std::promise<std::string>& prom) { try { // 模拟耗时解析 std::this_thread::sleep_for(std::chrono::milliseconds(500)); if (path.empty()) { throw std::runtime_error("file path is empty"); } std::string content = "<config><version>1.0</version></config>"; prom.set_value(content); // 成功结果送出去 return "ok"; } catch (...) { prom.set_exception(std::current_exception()); // 异常送出去 return "failed"; } } int main() { std::promise<std::string> prom; std::future<std::string> fut = prom.get_future(); std::thread worker(parse_file, "config.xml", std::ref(prom)); // 主线程继续做自己的事情 std::this_thread::sleep_for(std::chrono::milliseconds(200)); try { std::string result = fut.get(); std::cout << "parsed: " << result << std::endl; } catch (const std::exception& e) { std::cout << "got exception: " << e.what() << std::endl; } worker.join(); return 0; }

几个关键点逐一说一下。

prom.set_value()会立刻让 future 处于 ready 状态。注意它和条件变量 notify 不一样的地方:set_value 不会使 promise 失效,promise 后续还能析构,只是这个 future 已经拿到值了。

std::current_exception()是捕获当前异常并封装成异常指针的标准方式。future.get() 在调用线程里会重新抛出这个异常。这样就把“异常跨线程传递”这件事也搞定了。很多面试题里都会有这么一小段,题目是“如何把异常从一个线程传递到另一个线程”,promise 的 set_exception 就是标准答案。

如果 parse_file 里没有 catch……也会导致线程崩溃吗?不会。留意上面代码中 catch 的写法,它捕获了所有异常,避免异常从线程逃离导致程序 terminate。实际项目里线程函数的异常逃逸会导致整个进程崩溃,这一点千万注意。更稳妥的写法是把它包在线程入口函数最外层。

3.3 引入 std::shared_future:让多个消费者都能拿到结果

future.get() 只能调用一次,第二次调用会抛std::future_error(error code 为future_already_retrieved)。如果你有多个线程都想知道计算结果呢?比如一个任务完成后,主线程要展示结果,另一个监控线程也要记录结果。

解决方案是使用std::shared_future:

std::promise<int> prom; std::shared_future<int> sf = prom.get_future(); // 三个线程各自调用 sf.get(),都能拿到 42

shared_future 可以从 future 显式转换而来(fut.share()),也可以直接由 promise.get_future() 得到 shared_future。它的 get() 可以调用多次,内部做了共享状态访问的保护。这个知识点在面试里有时候会以“future 和 shared_future 的区别”出现,属于送分题,但确实有候选人没准备到。

4. 常见面试题高频清单与深度解析

4.1 面试官问“promise 和 future 的关系”时,想听什么

很多候选人直接背定义:promise 负责设置值,future 负责获取值。这当然不算错,但面试官往往会接着问一句:“为什么一个类要做成两个?”

这时候要说清楚核心思想:读写权限分离。promise 持有的是生产者权限,future 持有的是消费者权限。在一个多线程程序里,你希望生产者只能写不能读,消费者只能读不能写。通过类型系统强制这种分工,能有效避免误操作。这是 std::promise/std::future 这个设计最值得讲的一点。

其次,它们共同操作的对象是“共享状态”(shared state)。这个状态是 promise 内部创建的,future 只是它的一个视图。理解了这一点,就不难回答后续的扩展问题了。

4.2 高频问题速查表

我梳理了面试中出现频率较高的几个 promise 问题,整理成下面这个表格,方便快速过一遍。

面试问题关键思路易错点
promise 和 future 是什么关系共享状态 + 读写权限分离只说一个是设置、一个是获取,不够深入
可以多次调用 set_value 吗第二次会抛 future_error需要 promise 内部状态只能一次转换
调用两次 future.get() 会怎样第二次抛 future_error有人误以为第二次会返回相同值
promise 析构后 future 会怎样future.get() 抛出 broken_promise 异常误以为会永久等待
如何把异常传到另一个线程set_exception + current_exception忘记捕获所有异常
promise 和条件变量的区别一次性 vs 持续状态,无锁接口 vs 锁+谓词混为一谈
promise 是线程安全的吗同一个 promise 上并发操作同一方法不安全;不同方法(get_future 和 set_value)有同步保证误以为完全线程安全
如何实现超时等待 futurewait_for 返回状态,timeout 时继续轮询忘记处理 timeout 分支
为什么要用 shared_future支持多次 get,广播结果误以为 future 可以自动共享
promise 能否和 async 混用async 内部就是 promise,混用通常是冗余没有抓住层次关系

4.3 几个容易翻车的追问

追问一:“set_value 之后,promise 还能再用吗?”

不能。状态已经从 pending 变成 ready,promise 对象本身没有“重置”能力。如果你想再传递一次新结果,需要创建新的 promise。这个特性对面试官来说是个很好的考察点,因为有人会误以为 promise 像变量一样可反复赋值。

追问二:“future.wait() 和 future.get() 有什么区别?”

wait() 只等待共享状态变 ready,不消费结果;get() 会返回结果或抛出异常,并使得 future 的共享状态被释放。如果只想等待任务完成而不关心结果,用 wait() 更合适。有些编码风格里,把 future 当作“事件信号”来用,只等不取,也算一种合理用法。

追问三:“如何在 future 上做超时?”

用fut.wait_for(seconds)或wait_until(deadline)。它们返回std::future_status:ready(结果就绪)、timeout(超时未就绪)、deferred(async 延迟执行模式)。注意,如果用 std::async(std::launch::deferred) 创建任务,wait_for 的行为会有点特殊——任务可能会在执行 get 时才真正启动。这个坑在面试中属于进阶问题。

5. 实战避坑:用完 std::promise 你一定踩过的几个坑

5.1 忘记设置 promise,线程集体挂起

最常见的问题就是异常分支忘了 set_value 或者 set_exception。代码在正常路径里 set_value 了,但异常路径直接 return,导致另一个线程在 future.get() 上永久阻塞。这种 bug 非常隐蔽,因为不是必现,只在特定数据下触发。

我的习惯是:在创建一个 promise 之后,立刻想清楚它的所有出口路径。凡是可能提前 return 的地方,都要决定好是 set_value、set_exception,还是让 promise 析构抛出 broken_promise。更多时候我会把 set_value/set_exception 封装在一个函数里,用 RAII 的方式保证任何路径下都能“履约”。

5.2 在线程池或容器中误把 promise 复制了

std::promise 是只能移动、不能复制的类型。如果你把 promise 塞进 std::vector 或者 std::queue,需要使用移动语义。常见错误是写了promises.push_back(prom),结果是编译错误。解决办法是 std::move 进去。在实际项目里,线程池的任务封装类往往会把 promise 保存为成员变量,这时要注意成员初始化方式,别不小心写成拷贝构造。

还有更隐蔽的:在 lambda 捕获列表里默认按值捕获,如果捕获了 promise,编译器也会报错。正确姿势是[prom = std::move(prom)]() mutable { ... },并且要注意 lambda 的执行周期和 promise 的生命周期要匹配,否则可能发生 promise 析构时任务还没执行的问题。

5.3 在持锁范围内阻塞 get,死锁一秒出现

如果 promise 的 set_value 发生在持锁的线程里,而 future.get() 又在另一个线程持同一把锁等待,就出现了经典死锁:

std::mutex mtx; std::promise<int> prom; std::future<int> fut = prom.get_future(); // 线程A { std::lock_guard<std::mutex> lk(mtx); int val = fut.get(); // 等待线程B set_value // 这里持锁等待,线程B无法进入 } // 线程B { std::lock_guard<std::mutex> lk(mtx); prom.set_value(1); }

这种死锁用条件变量同样会发生,但 promise 的写法更容易让人忽略锁的问题。因为 promise 本身不涉及锁,给人的直觉是“应该很安全”。实际上,当你把 promise 和其他互斥锁混用时,一定要检查锁的持有范围是否跨越了 get 的等待过程。

5.4 吞掉异常的“假成功”写法

有的项目代码图省事,在 catch 块里只写了std::cout << "error",没有调用 set_exception,然后 return。这样调用方在 future.get() 上会永远等。如果调用方恰好在自己的异常捕获里记录日志,那这个 bug 可能要过很久才能定位。

我的建议是:凡是涉及到 promise 的接口,函数签名上就明确结果类型——用std::variant<Result, std::exception_ptr>或者直接用 promise 本身作为输出参数。如果团队封装的逻辑比较复杂,可以在 promise 外层再包一层“结果容器”,保证内部状态机完备。

5.5 超时退出后,promise 怎么办?

如果你在消费者线程里这么做:

if (fut.wait_for(std::chrono::seconds(1)) == std::future_status::timeout) { // 超时了,决定不再等待 }

问题是:promise 不会因为你超时就自动取消,工作线程还在跑,它最后仍然会 set_value。如果这段代码所在的作用域随后不再调用 get(),那个结果就被白白丢弃了。这本身没问题,但要注意可移动对象(比如 std::promise<std::unique_ptr >)在这种情况下,结果对象可能在后台线程里被构造,然后因为 future 不再读取而被析构。如果 T 的析构有副作用(比如释放系统资源),这个时间点就不那么可控了。

一个更彻底的做法是:如果你确实不想再等了,可以直接把 future 丢弃,让 promise 端在 set 的时候发生“无人取货”。标准库并不会为此报错,只是资源释放的时间点会变晚。

5.6 记住“单消费者”这个心智模型

最后强调一下:future 是单消费者的。get() 一次之后,future 里就不会再有值了,再次 get() 会抛错。所以一旦你打算让多个模块共享计算结果,一定要提前使用 shared_future,不要总想着在 future 上做文章。这个决定影响的是接口设计,建议在创建 promise 时就想清楚:这个结果是一个人用,还是一群人用。

我在实际项目里通常这样选:结果只给调用方的,用 std::promise + std::future;结果要广播给多个订阅者的,用 std::promise + std::shared_future;如果任务本身就是“执行一个函数并拿返回值”,那我直接用 std::async 或 packaged_task 就够了,连 promise 的变量都省了。

6. 一个面试答法示范,拿去吧

如果面试官让你“介绍一下 std::promise 以及实际使用场景”,可以考虑按下面这个节奏展开,每一层都能体现思考深度。

第一层:一句话定性。promise 是 C++11 引入的模板类,用于在线程间异步传递结果或异常,配对的 future 负责消费结果。两者共享同一个内部状态,生产者通过 promise 写入,消费者通过 future 读取。

第二层:说设计思想。它把生产者和消费者的权限分离,让类型系统保证生产者不能读、消费者不能写。它是一次性通道,状态一旦 ready 不能再改,这种一次性语义经常用来表示“某个事件已经完成”或者“某个结果已经就绪”。

第三层:举一个实际项目场景。比如自研线程池,提交任务时创建一个 promise,把 future 返回给调用方,任务执行完毕后在线程池的工作线程里调用 set_value 或者 set_exception。这样调用方可以在等待的同时干别的事,而不是像 join 那样必须等待线程退出。

第四层:讲一个注意点。如果 promise 在未设置结果的情况下析构,future 会抛出 broken_promise 异常而不是永久阻塞。这个设计保证了“生产者消失”时消费者能得到明确反馈。

这样回答大概两三分钟,既有概念又有设计思想又有实践细节,一般能留下不错的印象。

最后再分享一个小技巧:如果你手头有一个现有项目,想快速检验自己对 promise 的理解牢不牢,就试着把它和条件变量两种方式分别实现同一个“单线程传数”程序,然后对比代码行数、锁的使用、以及如果忘记通知时两种写法的行为差异。亲眼看到 promise 版本因为没有标志位所以绝对不会出现“漏通知”的问题,比背十篇博客都管用。

相关新闻

STM32驱动CCS811气体传感器:基于I2C的eCO2与tVOC测量工程实践

STM32驱动CCS811气体传感器:基于I2C的eCO2与tVOC测量工程实践

简介&#xff1a;基于STM32与CCS811气体传感器的毕业设计工程源码包&#xff0c;面向嵌入式、物联网方向学习者。项目以STM32F10x为主控&#xff0c;通过I2C接口驱动CCS811传感器&#xff0c;实现CO2与VOCs浓度实时采集&#xff0c;包含连续测量、低功耗模式切换及数据读取等核…

2026/10/20 12:09:03 阅读更多 →
DeepSeek零基础入门:从数据清洗到文本生成,新手避坑指南

DeepSeek零基础入门:从数据清洗到文本生成,新手避坑指南

简介&#xff1a;这份资源是面向AI工具初学者与办公效率提升人群的DeepSeek使用教学文档&#xff0c;帮助读者快速掌握这款智能数据搜索与分析平台的核心操作。内容覆盖注册登录、数据导入清洗、分析可视化、文本生成与摘要、情感分析、语言翻译、任务自动化、工作流优化以及模…

2026/10/20 12:09:03 阅读更多 →
Java地图匹配工具:用HMM算法将GPS轨迹精准钉在道路上

Java地图匹配工具:用HMM算法将GPS轨迹精准钉在道路上

简介&#xff1a;这是一份基于GraphHopper实现的地图匹配&#xff08;Map Matching&#xff09;开源工程&#xff0c;面向地理信息系统&#xff08;GIS&#xff09;开发者、轨迹分析工程师及Java后端工程师&#xff0c;解决将原始GPS轨迹点精准捕捉至OpenStreetMap道路网络的核…

2026/10/20 12:09:03 阅读更多 →

最新新闻

MCP协议驱动智能体底座架构设计:从插件思维到接口思维

MCP协议驱动智能体底座架构设计:从插件思维到接口思维

1. 从“插件思维”到“接口思维”的认知跃迁大多数人第一次接触MCP&#xff08;Model Context Protocol&#xff0c;模型上下文协议&#xff09;时&#xff0c;脑子里蹦出来的第一个词就是“插件系统”。这也不奇怪&#xff0c;毕竟它的外在表现确实像插件&#xff1a;你写一个…

2026/10/20 12:45:32 阅读更多 →
从零搭建React企业级应用架构:REA分层设计与工程实践

从零搭建React企业级应用架构:REA分层设计与工程实践

1. 项目概述先说结论&#xff1a;REA不是某个框架的名字&#xff0c;也不是一个开源库的缩写&#xff0c;而是我近期在团队内部从零搭建并持续打磨的一套 React 企业级应用架构方案。全称我习惯叫它 React Enterprise Architecture&#xff0c;有人问为什么不叫别的名字&#x…

2026/10/20 12:45:32 阅读更多 →
Matlab实现贝叶斯优化Transformer多输入单输出时序预测

Matlab实现贝叶斯优化Transformer多输入单输出时序预测

最近整理时序预测这块的工作时&#xff0c;我发现一个问题&#xff1a;很多人一上来就搭LSTM&#xff0c;序列一长效果就拉胯&#xff1b;换了Transformer吧&#xff0c;又开始为超参数头疼。手工调参既累又不稳定&#xff0c;尤其是当输入特征有好几个、预测目标只有一个的时候…

2026/10/20 12:45:32 阅读更多 →
告别重复办公!OpenClaw 3.1.0 本地 AI 智能体部署实战

告别重复办公!OpenClaw 3.1.0 本地 AI 智能体部署实战

&#x1f4d6; 前言 本文面向 Windows 系统用户&#xff0c;系统梳理 OpenClaw 的标准化部署流程。全程无需输入任何命令行&#xff0c;所有操作均依托可视化向导完成&#xff0c;即便是零基础用户也能独立走完整套部署。文中同时汇总了高频报错的对应解决方案&#xff0c;力求…

2026/10/20 12:45:32 阅读更多 →
Context-Mode是什么?一文讲透上下文模式的三种落地形态

Context-Mode是什么?一文讲透上下文模式的三种落地形态

最近在给某智能助手做效果调优时&#xff0c;我天天被一个词轰炸&#xff1a;context-mode。测试同学说"上下文模式下的记忆老丢"&#xff0c;后端同事说"异步线程里 context 没传过去"&#xff0c;模型侧的人又说"提示词里的 context 结构得调整&quo…

2026/10/20 12:45:32 阅读更多 →
副业内容创业成本优化指南:小说、漫画、漫剧的降本增效实战

副业内容创业成本优化指南:小说、漫画、漫剧的降本增效实战

1. 副业内容创业的成本困局与破局思路1.1 为什么小说、漫画、漫剧成了副业热门赛道这两年身边搞副业的朋友越来越多&#xff0c;我观察到一个很明显的趋势&#xff1a;以前大家聊副业&#xff0c;张口就是开店、带货、跑腿&#xff0c;现在聊得最多的变成了内容创作。小说、漫画…

2026/10/20 12:44:31 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →