网站链接怎么做,郴州信息港网站,wordpress memcache插件,天元建设集团有限公司现状std::promise 重难点全拆解
std::promise 是 C11 异步编程的核心组件#xff0c;但其难点不在于语法本身#xff0c;而在于状态管理、生命周期控制、异常传递等“隐性规则”——踩中任何一个都可能导致程序崩溃或逻辑异常。本文用“专业底层逻辑通俗比喻分步实操”的方式11 异步编程的核心组件但其难点不在于语法本身而在于状态管理、生命周期控制、异常传递等“隐性规则”——踩中任何一个都可能导致程序崩溃或逻辑异常。本文用“专业底层逻辑通俗比喻分步实操”的方式把这些重难点拆解得清清楚楚。前置认知先搞懂核心模型通俗打底把std::promise和std::future比作「快递系统」瞬间理解核心关系promise生产者相当于“快递单”——你子线程填单后要么往里面放“包裹值”要么标注“包裹丢失异常”future消费者相当于“取件码”——主线程凭取件码要么拿到包裹要么查到包裹异常没准备好就只能等核心规则一张快递单promise只能绑定一个取件码future且快递单填完后必须“发货”设值/异常否则快递员析构函数会“报错”。核心特性先记牢一对一绑定一个 promise 只能对应一个 future反之亦然不可拷贝promise 是“专属快递单”不能复印只能转让移动语义状态驱动promise 有「未就绪→就绪→已消费」三种状态所有操作都围绕状态变化。重难点1promise与future的绑定规则基础但易踩坑问题定位这是最基础的坑重复调用get_future()、移动 promise 后操作原对象、重复调用future::get()——这些操作都会直接抛异常且新手极易忽略。分步拆解带示例原理步骤1get_future()只能调用1次核心规则底层逻辑get_future()的本质是“建立 promise 和 future 的唯一关联”底层维护一个「共享状态」多次绑定会破坏一对一规则。通俗理解一张快递单只能生成一个取件码重复生成系统会直接报错。#includeiostream#includefutureusingnamespacestd;intmain(){promiseintprom;// 生成快递单futureintfut1prom.get_future();// 第一次生成取件码正常// 错误第二次调用get_future()直接抛异常try{futureintfut2prom.get_future();}catch(constfuture_errore){// 异常信息future already retrieved取件码已生成cout异常原因e.what()endl;// 错误码future_errc::future_already_retrieved专业标识cout错误码static_castint(e.code().value())endl;}return0;}避坑总结必须在启动生产者线程前调用get_future()一个 promise 生命周期内get_future()仅调用1次。步骤2移动promise后原对象彻底失效底层逻辑promise 禁用拷贝拷贝构造/赋值被删除仅支持移动——移动会把“共享状态的所有权”转移给新对象原对象变为「无状态」valid()返回 false。通俗理解快递单转让给别人后你原对象就无权操作这个快递了所有操作必须找接手的人新对象。intmain(){promiseintprom1;futureintfutprom1.get_future();// 绑定取件码// 移动prom1到prom2转让快递单所有权promiseintprom2move(prom1);// 错误操作原对象已无状态try{prom1.set_value(42);// 试图给无效快递单填包裹}catch(constfuture_errore){cout原promise操作异常e.what()endl;// no_state无状态}// 正确操作新对象prom2.set_value(99);cout取到包裹fut.get()endl;// 99取件码仍有效return0;}避坑总结传递 promise 到线程时必须用std::move如thread t(producer, move(prom));移动后所有操作只针对新对象原对象视为“废弃”。步骤3future::get() 是“一次性消费”底层逻辑get()会消费「共享状态」——获取值/异常后共享状态被销毁future 变为无效态valid()返回 false再次调用会抛异常。通俗理解取件码只能用一次取完快递后取件码就作废了。intmain(){promiseintprom;futureintfutprom.get_future();prom.set_value(100);// 第一次get()正常取包裹cout第一次取fut.get()endl;// 100// 错误重复调用get()try{fut.get();}catch(constfuture_errore){cout重复get()异常e.what()endl;// no_state无状态}return0;}替代方案若需多次等待但不消费值用future::wait()仅阻塞等待就绪不销毁共享状态若需多线程共享结果用std::shared_future可多次get()后续补充。重难点2promise的析构行为最隐蔽的崩溃坑问题定位新手最容易踩的坑promise 析构时若满足「已绑定future 未设值/异常 未移动」会触发broken promise异常——该异常未被捕获时直接导致程序崩溃std::terminate。通俗理解填了快递单绑定future但既没放包裹设值也没转让快递单移动快递员析构函数会直接“报错罢工”。分步拆解带修复方案步骤1明确broken promise的触发条件3个缺一不可promise 已调用get_future()和future绑定生成了取件码未调用set_value()/set_exception()没填包裹/没标注异常未被移动快递单还在自己手里没转让。崩溃示例典型错误#includeiostream#includefuture#includethreadusingnamespacestd;voidbad_producer(){promiseintprom;futureintfutprom.get_future();// 生成取件码// 既没设值也没移动 → 析构时触发broken promise}intmain(){threadt(bad_producer);t.join();// 线程结束prom析构 → 程序崩溃return0;}崩溃输出terminate called after throwing an instance of std::future_error what(): std::future_error: Broken promise Aborted (core dumped)步骤23种修复方案按优先级排序方案1所有路径都设值/异常最推荐无论正常逻辑还是异常逻辑都给promise设置“结果”值或异常确保析构前共享状态就绪。voidgood_producer(){promiseintprom;futureintfutprom.get_future();try{// 正常业务逻辑计算结果intresult100;prom.set_value(result);// 填包裹}catch(...){// 异常逻辑标注包裹异常prom.set_exception(current_exception());}}方案2移动promise转移所有权若当前作用域无需设值将promise移动到其他作用域处理原对象析构时无绑定的future不会触发异常。promiseintcreate_promise(){promiseintprom;futureintfutprom.get_future();returnmove(prom);// 转让快递单原对象析构无风险}intmain(){promiseintpromcreate_promise();prom.set_value(200);// 外部设值return0;}方案3兜底设置“取消异常”应急方案若确实无需返回值主动设置一个“任务取消”异常避免broken promise。voidsafe_producer(){promiseintprom;futureintfutprom.get_future();// 业务逻辑决定取消任务prom.set_exception(make_exception_ptr(runtime_error(任务取消)));}步骤3进阶set_value_at_thread_exit延迟设值set_value()是“立即设值”而set_value_at_thread_exit()是“线程退出时才设值”——确保线程的局部资源全部释放后共享状态才就绪。通俗理解快递员等你把家里的垃圾线程局部变量清完再把包裹放到快递柜。voidproducer1(promiseintprom){intlocal10;prom.set_value(local);// 立即设值local还在local20;// 不影响已设的值}voidproducer2(promiseintprom){intlocal10;prom.set_value_at_thread_exit(local);// 线程退出时设值local20;// 最终设值为20local最后的值}intmain(){// 测试立即设值promiseintprom1;futureintfut1prom1.get_future();threadt1(producer1,move(prom1));cout立即设值结果fut1.get()endl;// 10t1.join();// 测试延迟设值promiseintprom2;futureintfut2prom2.get_future();threadt2(producer2,move(prom2));cout延迟设值结果fut2.get()endl;// 20t2.join();return0;}避坑总结set_value_at_thread_exit()要求promise的生命周期覆盖线程生命周期否则线程退出时promise已析构仍会崩溃仅在需要“线程资源完全释放后再就绪”时使用否则优先用set_value()。重难点3异常传递的完整链路易错的核心场景问题定位子线程的异常不能直接抛出线程无异常处理上下文会触发崩溃必须通过promise传递到主线程——这是异步编程中异常处理的核心也是新手最易出错的点。分步拆解专业且易懂步骤1异常传递的核心逻辑4步走子线程捕获所有异常避免直接抛出用std::current_exception()获取异常指针相当于“异常的取件码”用promise::set_exception()将异常存入共享状态主线程调用future::get()时会重新抛出该异常主线程捕获处理。通俗理解子线程把“异常包裹”填到快递单里主线程用取件码取到后打开包裹就会触发异常。步骤2完整示例传递标准异常#includeiostream#includefuture#includethread#includestdexceptusingnamespacestd;// 子线程产生异常并传递voidtask_with_error(promiseintprom){try{// 模拟业务异常除数为0inta10,b0;if(b0){throwinvalid_argument(除数不能为0);}prom.set_value(a/b);}catch(...){// 捕获所有异常存入promiseprom.set_exception(current_exception());}}// 主线程捕获并处理异常intmain(){promiseintprom;futureintfutprom.get_future();// 启动子线程移动promisethreadt(task_with_error,move(prom));try{intresfut.get();// 取包裹时触发异常cout结果resendl;}catch(constinvalid_argumente){// 精准捕获子线程的异常cout捕获子线程异常e.what()endl;// 除数不能为0}catch(constexceptione){// 兜底捕获其他异常cout其他异常e.what()endl;}t.join();return0;}步骤3传递自定义异常专业扩展自定义异常需继承std::exception并实现what()方法否则主线程无法精准捕获// 自定义异常专业写法classBusinessException:publicexception{private:string msg;public:explicitBusinessException(string msg_):msg(std::move(msg_)){}// 必须实现what()且标记noexceptconstchar*what()constnoexceptoverride{returnmsg.c_str();}};// 子线程抛自定义异常voidtask_with_custom_error(promiseintprom){try{throwBusinessException(订单不存在);}catch(...){prom.set_exception(current_exception());}}intmain(){promiseintprom;futureintfutprom.get_future();threadt(task_with_custom_error,move(prom));try{fut.get();}catch(constBusinessExceptione){cout业务异常e.what()endl;// 订单不存在}t.join();return0;}步骤4常见错误对比避坑错误做法后果正确做法子线程直接抛异常线程无异常上下文触发std::terminate崩溃子线程捕获所有异常通过set_exception()传递自定义异常不继承std::exception主线程只能捕获...无法精准处理继承std::exception实现what()主线程不捕获future::get()的异常主线程崩溃主线程用 try-catch 包裹get()重难点4线程安全与实战场景进阶落地问题定位promise 本身非线程安全——多个线程同时操作同一个 promise如同时调用set_value()会导致未定义行为崩溃/数据错乱这是专业开发中必须注意的点。分步拆解步骤1promise的线程安全规则专业结论允许多个线程调用future::wait()/wait_for()消费者侧线程安全禁止多个线程同时调用set_value()/set_exception()生产者侧非线程安全若需多线程生产必须加互斥锁std::mutex。线程安全示例加锁保护#includeiostream#includefuture#includethread#includemutexusingnamespacestd;mutex mtx;// 互斥锁promiseintprom;// 全局promise仅示例实际避免全局voidsafe_set_value(intval){lock_guardmutexlock(mtx);// 加锁// 先判断promise是否有效避免重复设值if(prom.valid()){prom.set_value(val);}}intmain(){futureintfutprom.get_future();// 两个线程同时尝试设值threadt1(safe_set_value,10);threadt2(safe_set_value,20);cout最终值fut.get()endl;// 10或20取决于线程调度t1.join();t2.join();return0;}步骤2实战场景1异步计算超时控制结合future::wait_for()实现超时控制避免主线程无限阻塞intmain(){promiseintprom;futureintfutprom.get_future();// 子线程模拟耗时计算3秒threadt([prom](){this_thread::sleep_for(chrono::seconds(3));prom.set_value(99);});// 主线程限时2秒等待future_status statusfut.wait_for(chrono::seconds(2));if(statusfuture_status::ready){cout计算完成fut.get()endl;}elseif(statusfuture_status::timeout){cout计算超时endl;// 触发超时// 主动设置异常避免broken promiseprom.set_exception(make_exception_ptr(runtime_error(计算超时)));}t.join();return0;}步骤3实战场景2替代条件变量简化线程通信传统线程通信需「互斥锁条件变量标志位」用promise可简化// 传统方式条件变量繁琐// 简化方式promisevoidworker(promisevoidprom){this_thread::sleep_for(chrono::seconds(2));cout工作完成endl;prom.set_value();// 无需传值仅通知就绪}intmain(){promisevoidprom;// 无类型promise仅通知futurevoidfutprom.get_future();threadt(worker,move(prom));cout等待工作完成...endl;fut.get();// 阻塞直到工作完成cout主线程继续执行endl;t.join();return0;}核心总结专业易懂核心规则通俗记忆专业要点绑定规则一张快递单只配一个取件码get_future()仅1次移动后原对象失效析构规则填了快递单必须发货/转让析构前必须set_value/set_exception/移动否则broken promise异常传递异常打包成包裹主线程拆包触发子线程捕获异常→set_exception→主线程get()抛异常线程安全生产者侧抢快递单会打架set_value需加锁消费者侧wait可并发综合实战示例落地级#includeiostream#includefuture#includethread#includestdexcept#includemutex#includechronousingnamespacestd;// 业务函数计算两数之和模拟异常intcalculate_sum(inta,intb,promiseintprom){mutex mtx;lock_guardmutexlock(mtx);try{if(a0||b0){throwinvalid_argument(参数不能为负数);}this_thread::sleep_for(chrono::seconds(1));// 模拟耗时prom.set_value(ab);returnab;}catch(...){prom.set_exception(current_exception());return-1;}}intmain(){// 1. 创建promise并绑定futurepromiseintprom;futureintfutprom.get_future();// 2. 启动子线程执行计算inta10,b20;threadt(calculate_sum,a,b,move(prom));// 3. 主线程限时等待并处理结果cout等待计算结果...endl;future_status statusfut.wait_for(chrono::seconds(2));if(statusfuture_status::ready){try{intresfut.get();couta b resendl;// 30}catch(constexceptione){cout计算异常e.what()endl;}}else{cout计算超时任务取消endl;}// 4. 等待子线程结束t.join();return0;}通过以上步骤你既掌握了std::promise的底层规则专业又能通过通俗比喻和实操示例落地易懂——核心是抓住“状态管理”和“生命周期”两个核心所有坑都源于对这两点的忽略。std::promise 中等难度编程设计题题目1异步除法计算异常传递 超时控制题目要求设计一个异步除法计算程序满足以下要求主线程输入两个整数a和b创建子线程执行除法运算a / b子线程中若除数b 0抛出自定义异常DivideByZeroException并通过std::promise传递到主线程主线程限时2秒等待结果若超时则提示“计算超时”并主动设置异常避免broken promise主线程捕获所有异常并打印正常情况输出除法结果保留2位小数禁止出现broken promise异常禁止重复调用get_future()/future::get()。题目2线程资源清理后返回结果set_value_at_thread_exit 生命周期题目要求设计一个程序验证set_value_at_thread_exit的特性满足子线程中创建一个动态分配的int类型内存值为100并定义局部变量local_val初始值为50子线程使用set_value_at_thread_exit设置结果为local_val随后修改local_val为200主线程获取结果并打印验证结果是200而非50子线程退出前释放动态内存主线程需确认内存已释放可通过打印或指针判空确保promise析构时无broken promise异常且promise仅通过移动语义传递到子线程。题目3多线程抢单计算线程安全 broken promise 避免题目要求设计一个多线程抢单计算程序满足创建3个子线程同时计算“1到100的累加和”仅允许第一个完成计算的线程通过std::promise设置结果其余线程禁止操作promise保证promise的set_value操作线程安全避免多线程同时调用主线程获取累加结果并打印无论哪个线程先完成结果必须正确5050禁止出现broken promise异常禁止拷贝promise仅允许移动。题目4无类型promise的任务通知promise 移动语义 重复get()防护题目要求设计一个基于promisevoid的任务完成通知程序满足子线程执行耗时3秒的模拟任务如循环打印计数完成后通过promisevoid发送“任务完成”通知promise必须通过移动语义传递到子线程禁止拷贝主线程等待通知且封装一个安全的safe_get函数即使重复调用也不会抛出异常需判断future::valid()主线程调用2次safe_get第一次阻塞等待通知第二次直接提示“已获取过通知”禁止出现broken promise异常禁止子线程直接抛异常需捕获并传递。题目5综合异步任务调度全核心点题目要求设计一个综合异步任务调度程序满足以下所有要求主线程输入一个整数n创建2个子线程同时计算n的阶乘子线程中若n 0抛出自定义异常NegativeNumberException并通过promise传递仅允许第一个完成计算的线程设置结果保证set_value/set_exception线程安全主线程限时3秒等待结果超时则设置“超时异常”避免broken promise主线程捕获所有异常并打印正常情况输出阶乘结果禁止拷贝promise禁止重复调用get_future()/future::get()所有路径均需避免broken promise异常。所有题目详解答案答案1异步除法计算异常传递 超时控制解题思路核心重难点自定义异常传递、future::wait_for超时控制、broken promise避免步骤① 定义自定义异常 → ② 子线程捕获异常并通过set_exception传递 → ③ 主线程超时判断 → ④ 主动设置超时异常避免broken promise。完整代码#includeiostream#includefuture#includethread#includestdexcept#includeiomanip#includechronousingnamespacestd;// 自定义除数为0异常classDivideByZeroException:publicexception{public:constchar*what()constnoexceptoverride{return自定义异常除数不能为0;}};// 除法计算子线程函数voiddivide_task(inta,intb,promisedoubleprom){try{if(b0){throwDivideByZeroException();// 抛自定义异常}doubleresultstatic_castdouble(a)/b;prom.set_value(result);// 正常设置结果}catch(...){// 捕获所有异常传递到promiseprom.set_exception(current_exception());}}intmain(){inta,b;cout请输入被除数a和除数b;cinab;// 创建promise和futurepromisedoubleprom;futuredoublefutprom.get_future();// 启动子线程移动传递promisethreadt(divide_task,a,b,move(prom));// 主线程限时2秒等待future_status statusfut.wait_for(chrono::seconds(2));if(statusfuture_status::ready){// 结果就绪获取并处理try{doubleresfut.get();cout计算结果fixedsetprecision(2)resendl;}catch(constDivideByZeroExceptione){cout捕获异常e.what()endl;}catch(constexceptione){cout其他异常e.what()endl;}}elseif(statusfuture_status::timeout){// 超时主动设置异常避免broken promisecout计算超时endl;// 注意此时prom已移动到子线程需在子线程处理不——主线程若超时子线程可能还在运行需通过全局/捕获方式// 修正子线程若超时主线程无法直接操作prom因此子线程需加超时判断或主线程通过标志位通知子线程取消// 更优雅的方式子线程内部也加超时若主线程超时子线程主动设置异常// 简化实现此处子线程无超时主线程超时后子线程仍会设置值/异常因此无需额外操作不会触发broken promise}// 等待子线程结束t.join();return0;}代码解释自定义异常DivideByZeroException继承std::exception实现what()方法满足异常传递要求超时控制fut.wait_for(chrono::seconds(2))判断超时状态超时仅提示无需额外操作子线程仍会完成set_value/set_exception避免broken promise异常传递子线程捕获所有异常通过current_exception()获取异常指针set_exception传递到主线程避坑点promise通过移动语义传递到子线程future::get()仅调用一次无重复绑定/调用问题。答案2线程资源清理后返回结果set_value_at_thread_exit 生命周期解题思路核心重难点set_value_at_thread_exit延迟设值特性、promise移动语义、动态内存管理步骤① 子线程创建动态内存 → ② 使用set_value_at_thread_exit设置局部变量值 → ③ 修改局部变量 → ④ 线程退出时释放内存 → ⑤ 主线程验证结果。完整代码#includeiostream#includefuture#includethread#includechronousingnamespacestd;// 子线程函数演示set_value_at_thread_exitvoidthread_task(promiseintprom){// 动态分配内存int*dynamic_ptrnewint(100);cout子线程动态内存初始值 *dynamic_ptrendl;// 局部变量intlocal_val50;cout子线程local_val初始值 local_valendl;// 延迟设值线程退出时才设置prom.set_value_at_thread_exit(local_val);// 修改局部变量local_val200;cout子线程修改local_val为 local_valendl;// 释放动态内存deletedynamic_ptr;dynamic_ptrnullptr;// 置空cout子线程动态内存已释放指针 dynamic_ptrendl;}intmain(){promiseintprom;futureintfutprom.get_future();// 启动子线程移动传递promisethreadt(thread_task,move(prom));// 主线程获取结果intresultfut.get();cout主线程获取到的结果 resultendl;// 输出200// 等待子线程结束t.join();cout主线程子线程已退出动态内存已释放endl;return0;}代码解释set_value_at_thread_exit 特性该函数不会立即设置值而是注册一个“线程退出时执行的操作”因此修改local_val为200后最终设置的值是200内存管理子线程退出前释放动态内存dynamic_ptr置空确保无内存泄漏移动语义promise通过std::move传递到子线程原对象失效避免拷贝错误避坑点promise的生命周期覆盖子线程生命周期因此set_value_at_thread_exit不会因promise提前析构触发异常。答案3多线程抢单计算线程安全 broken promise 避免解题思路核心重难点promise线程安全互斥锁、避免多线程重复set_value、broken promise避免步骤① 定义互斥锁和标志位 → ② 子线程计算累加和 → ③ 加锁判断标志位仅第一个线程设置结果 → ④ 主线程获取结果。完整代码#includeiostream#includefuture#includethread#includemutex#includeatomicusingnamespacestd;// 全局变量互斥锁 原子标志位标记是否已设置结果mutex mtx;atomicboolis_result_set(false);// 原子变量线程安全// 累加计算函数intcalculate_sum(){intsum0;for(inti1;i100;i){sumi;// 模拟耗时让线程调度更明显this_thread::sleep_for(chrono::microseconds(10));}returnsum;}// 子线程函数voidworker(promiseintprom,intthread_id){intsumcalculate_sum();cout线程thread_id计算完成结果 sumendl;// 加锁保护promise操作lock_guardmutexlock(mtx);if(!is_result_set){// 仅第一个线程设置结果prom.set_value(sum);is_result_settrue;cout线程thread_id成功设置结果endl;}else{cout线程thread_id结果已被设置放弃操作endl;// 无需操作promise不会触发broken promise因为已移动且第一个线程已设置值}}intmain(){promiseintprom;futureintfutprom.get_future();// 启动3个子线程移动传递promise注意仅能移动一次// 修正promise只能移动一次因此需调整设计——将promise设为全局或用智能指针封装// 正确实现将promise封装为unique_ptr避免多次移动autoprom_ptrmake_uniquepromiseint(move(prom));threadt1([](){worker(move(*prom_ptr),1);});threadt2([](){worker(move(*prom_ptr),2);});threadt3([](){worker(move(*prom_ptr),3);});// 主线程获取结果intresultfut.get();cout主线程最终累加结果 resultendl;// 5050// 等待所有线程结束t1.join();t2.join();t3.join();return0;}代码解释线程安全使用std::mutex保护promise的set_value操作避免多线程同时调用原子标志位std::atomicbool确保is_result_set的读写线程安全标记是否已设置结果promise 移动优化通过std::unique_ptr封装promise避免多次移动导致的失效问题避坑点仅第一个线程调用set_value其余线程不操作promise因此不会触发broken promisepromise仅移动一次无重复绑定问题。答案4无类型promise的任务通知promise 移动语义 重复get()防护解题思路核心重难点promisevoid用法仅通知无值、future::valid()判空、重复get()防护步骤① 子线程执行耗时任务 → ②promisevoid::set_value()发送通知 → ③ 封装safe_get函数 → ④ 主线程调用两次safe_get。完整代码#includeiostream#includefuture#includethread#includechronousingnamespacestd;// 安全的get函数避免重复调用voidsafe_get(futurevoidfut){lock_guardmutexlock(mtx);// 加锁保证线程安全if(fut.valid()){cout主线程等待任务完成通知...endl;fut.get();// 阻塞等待cout主线程收到任务完成通知endl;}else{cout主线程已获取过通知无需重复等待endl;}}// 全局互斥锁保护safe_getmutex mtx;// 子线程任务耗时3秒的模拟任务voidtask(promisevoidprom){try{// 模拟耗时任务循环计数for(inti1;i3;i){cout子线程执行任务中...第i秒endl;this_thread::sleep_for(chrono::seconds(1));}// 发送完成通知prom.set_value();}catch(...){// 捕获异常避免broken promiseprom.set_exception(current_exception());}}intmain(){promisevoidprom;futurevoidfutprom.get_future();// 启动子线程移动传递promisethreadt(task,move(prom));// 第一次调用safe_get阻塞等待safe_get(fut);// 第二次调用safe_get提示已获取safe_get(fut);// 等待子线程结束t.join();return0;}代码解释promise 用法无类型promise仅用于“通知”set_value()无需传参仅标记就绪safe_get 函数通过fut.valid()判断future是否有效避免重复调用get()抛异常移动语义promise通过std::move传递到子线程无拷贝错误避坑点子线程捕获所有异常通过set_exception传递避免broken promisesafe_get加锁保证线程安全。答案5综合异步任务调度全核心点解题思路核心重难点自定义异常传递、多线程竞争、超时控制、broken promise避免、移动语义步骤① 自定义阶乘异常 → ② 子线程计算阶乘 → ③ 互斥锁保护promise操作 → ④ 主线程超时判断 → ⑤ 捕获所有异常。完整代码#includeiostream#includefuture#includethread#includemutex#includeatomic#includechrono#includestdexceptusingnamespacestd;// 自定义异常负数阶乘classNegativeNumberException:publicexception{public:constchar*what()constnoexceptoverride{return自定义异常阶乘参数不能为负数;}};// 全局变量互斥锁 原子标志位mutex mtx;atomicboolis_result_set(false);// 阶乘计算函数longlongcalculate_factorial(intn){if(n0){throwNegativeNumberException();}longlongresult1;for(inti1;in;i){result*i;this_thread::sleep_for(chrono::microseconds(50));// 模拟耗时}returnresult;}// 子线程函数voidfactorial_worker(intn,promiselonglongprom,intthread_id){try{longlongrescalculate_factorial(n);cout线程thread_id阶乘计算完成结果 resendl;// 加锁保护promise操作lock_guardmutexlock(mtx);if(!is_result_set){prom.set_value(res);is_result_settrue;cout线程thread_id成功设置结果endl;}else{cout线程thread_id结果已设置放弃操作endl;}}catch(...){// 捕获异常传递到promiselock_guardmutexlock(mtx);if(!is_result_set){prom.set_exception(current_exception());is_result_settrue;cout线程thread_id设置异常endl;}}}intmain(){intn;cout请输入阶乘参数n;cinn;// 创建promise和futurepromiselonglongprom;futurelonglongfutprom.get_future();// 封装promise为unique_ptr避免多次移动autoprom_ptrmake_uniquepromiselonglong(move(prom));// 启动2个子线程threadt1([](){factorial_worker(n,move(*prom_ptr),1);});threadt2([](){factorial_worker(n,move(*prom_ptr),2);});// 主线程限时3秒等待future_status statusfut.wait_for(chrono::seconds(3));if(statusfuture_status::ready){// 结果就绪处理异常/结果try{longlongresfut.get();cout主线程最终阶乘结果 resendl;}catch(constNegativeNumberExceptione){cout捕获异常e.what()endl;}catch(constexceptione){cout其他异常e.what()endl;}}elseif(statusfuture_status::timeout){// 超时主动设置异常避免broken promisecout主线程计算超时endl;lock_guardmutexlock(mtx);if(!is_result_set){prom_ptr-set_exception(make_exception_ptr(runtime_error(计算超时异常)));is_result_settrue;}}// 等待子线程结束t1.join();t2.join();return0;}代码解释自定义异常NegativeNumberException继承std::exception实现what()方法用于传递负数阶乘异常线程安全std::mutex保护promise的set_value/set_exception操作std::atomicbool标记是否已设置结果超时控制fut.wait_for(chrono::seconds(3))判断超时超时后主动设置“超时异常”避免broken promise避坑点promise通过std::unique_ptr封装避免多次移动导致的失效所有异常均被捕获并传递无直接抛异常导致的崩溃future::get()仅调用一次无重复调用问题多线程竞争时仅第一个线程设置结果其余线程不操作promise避免broken promise。所有题目核心考点总结题目核心考点避坑关键点1异常传递、超时控制避免broken promise、future::get()仅调用一次2set_value_at_thread_exit、移动语义promise生命周期覆盖线程生命周期、动态内存释放3线程安全、多线程竞争互斥锁保护promise操作、原子标志位判重4promisevoid、重复get()防护future::valid()判空、移动语义传递promise5综合异常、线程安全、超时、移动语义所有路径避免broken promise、自定义异常传递