内容提要
本文介绍了C++11中的并发编程,包括线程、互斥量、条件变量和期物等概念。使用std::thread创建和管理线程,std::mutex保护临界区,std::condition_variable解决线程间同步问题。期物(std::future)和承诺(std::promise)用于处理异步任务的结果。
延伸解读
从手动锁到RAII:锁管理的演进
文章指出,直接调用mutex的lock/unlock容易在异常时忘记解锁,因此推荐使用std::lock_guard。它基于RAII,在构造时加锁、析构时解锁,保证异常安全。而std::unique_lock更灵活,允许显式调用lock/unlock,可缩小锁范围,并且在使用条件变量时是必需的。这体现了C++资源管理从手动到自动的演进,读者应根据场景选择:简单临界区用lock_guard,需要灵活控制或配合条件变量时用unique_lock。
条件变量:避免忙等待与死锁
文章解释,条件变量用于线程间同步,避免忙等待导致的死锁。生产者-消费者示例中,消费者使用cv.wait(lk, predicate)等待条件成立,并处理虚假唤醒。文章还提到,在多消费者场景下,notify_one可能不如notify_all,但即使如此,由于mutex的排他性,多个消费者也无法真正并行消费,需要更细粒度的同步手段。这提醒读者,条件变量虽能解决同步问题,但性能优化需考虑锁的粒度和通知策略。
期物与承诺:异步结果的优雅获取
文章对比了C++11之前通过全局变量和事件获取异步结果的方式,指出std::future和std::promise简化了这一流程。std::packaged_task封装可调用对象,通过get_future获得future,实现异步调用。std::promise则用于设置值或异常,与future配合形成生产者-消费者模式。文章还展示了std::async和wait_for的用法,以及如何通过set_exception传递异常。这些工具让异步编程更安全、更简洁,但需注意future的get()只能调用一次。
原子操作与内存模型:性能与一致性的权衡
文章强调,std::atomic将原子操作最小化到单条CPU指令,但并非所有类型都支持,可用is_lock_free检查。内存模型定义了六种内存顺序,对应四种一致性模型:宽松、释放/消费、释放/获取和顺序一致。宽松模型性能最好但同步最弱,顺序一致最安全但可能损耗性能。文章通过示例说明,开发者应根据需求选择合适的内存顺序,在保证正确性的前提下追求性能。这要求对多线程同步有深入理解。
Q&A
C++11中如何创建和管理线程?
可以使用std::thread类来创建和管理线程,使用构造函数传入线程执行体,并通过join()或detach()管理线程生命周期。
什么是std::mutex,它的作用是什么?
std::mutex是C++11中用于保护临界区的互斥量类,提供lock()和unlock()方法,确保在多线程环境下的安全访问。
如何使用条件变量解决线程间的同步问题?
可以使用std::condition_variable,通过notify_one()或notify_all()唤醒等待的线程,解决线程间的同步问题。
期物和承诺在C++11中有什么作用?
期物(std::future)用于获取异步任务的结果,而承诺(std::promise)用于设置异步操作的返回值或异常,二者结合实现生产者-消费者模式。
C++11的内存模型如何确保多线程的安全性?
C++11的内存模型提供了原子操作和一致性模型,确保在多线程环境下的安全性和性能,避免数据竞争。
std::atomic在C++11中有什么用?
std::atomic提供了原子类型,允许在多线程环境中安全地进行读写操作,避免数据竞争和不一致性。