对《CPP-Concurrency-In-Action-2ed-2019》可中断线程实现的三个技术疑问
关于《CPP-Concurrency-In-Action-2ed-2019》9.2.1章节interruptible_thread实现的疑问解答
先贴出原示例代码:
class interrupt_flag { public: void set(); bool is_set() const; }; thread_local interrupt_flag this_thread_interrupt_flag; // 1 class interruptible_thread { std::thread internal_thread; interrupt_flag* flag; public: template<typename FunctionType> interruptible_thread(FunctionType f) { std::promise<interrupt_flag*> p; // 2 internal_thread=std::thread([f,&p]{ // 3 p.set_value(&this_thread_interrupt_flag); f(); // 4 }); flag=p.get_future().get(); // 5 } void interrupt() { if(flag) { flag->set(); // 6 } } };
针对代码的三个疑问,解答如下:
疑问1:若新线程中的p.set_value()在interruptible_thread构造函数之后执行,作为构造函数局部变量的std::promise p是否会成为悬空引用?
不会。构造函数中调用的p.get_future().get()是阻塞调用,会一直等待对应的std::promise执行set_value()后才返回。也就是说,构造函数会卡在第5行,直到新线程完成p.set_value(&this_thread_interrupt_flag)的调用,才会继续执行后续逻辑、结束构造函数并销毁p。新线程对p的引用全程有效,不会出现悬空问题。
疑问2:使用std::promise p的目的是什么?直接传入flag并赋值flag=&this_thread_interrupt_flag是否能达到同样效果?
不能直接这么做。核心原因是this_thread_interrupt_flag是**线程局部存储(thread_local)**变量,每个线程拥有独立的实例:
- 如果直接在lambda中给
flag赋值,比如[f, this]{ this->flag = &this_thread_interrupt_flag; f(); },这里的this_thread_interrupt_flag会是构造函数所在线程的实例,而非新线程的——lambda的上下文属于构造函数线程,无法直接访问新线程的thread_local变量。 std::promise的作用是让新线程在自己的上下文里获取到属于自身的this_thread_interrupt_flag地址,再通过promise将该地址传递回构造函数线程,最终赋值给interruptible_thread的flag成员。这样flag才会指向新线程独有的中断标志,确保后续interrupt()调用能正确标记目标线程。
疑问3:调用interrupt()时如何中断新线程?我并未看到相关的触发逻辑?
这个示例仅实现了中断标志的传递机制,完整的中断逻辑需要任务函数f()主动协作:
用户需要在任务函数的合适检查点(比如循环末尾、IO操作前后),主动判断this_thread_interrupt_flag.is_set()的状态。比如任务函数可以写成:
void my_task() { while (!this_thread_interrupt_flag.is_set()) { // 执行任务逻辑 // 定期检查中断标志 } }
当调用interrupt()时,会设置新线程对应的interrupt_flag,任务函数检测到标志被设置后,即可主动退出,从而实现“中断”效果。C++标准库没有提供强制终止线程的安全机制,这种协作式中断是避免资源泄漏、数据不一致的推荐方案。
内容的提问来源于stack exchange,提问作者f1msch
相关产品推荐
相关产品推荐

