面经——线程切换开销、弱引用智能指针weak_ptr

本文详细探讨了线程上下文切换的开销,包括CPU寄存器保存、系统调度器执行等直接和间接消耗,并介绍了减少上下文切换的四种方法:无锁并发编程、锁分离技术、CAS算法和最小化线程使用。此外,文章还讲解了C++中弱引用智能指针`weak_ptr`的用法,解释了其避免循环引用和内存泄漏的作用。

这段时间一直在忙着秋招,一边面试,一边整理总结,在面试的过程中也会发现很多问题,自己学习的时候并没有深究,导致“知其然而不知其所以然”,当面试被问到的时候,感到很可惜。其实想想也不必可惜,面试就是一个查漏补缺的过程,在这个过程中发现自己的一些遗漏之处,进一步查阅资料、深入学习,对自己也是很有帮助的。

现在对自己遇到的以及和周围的小伙伴交流的他们遇到的其中一些问题,整理下面经,必要时留下有参考意义的前辈的文章,以便后续查看。

一、线程的上下文切换及其开销

在一次面试时,被问到线程切换的开销指的是什么?听到这个问题的时候有点懵懵的,最后也没答出来个所以然,今天就对这个问题做一些整理。

多线程中两个必要的开销:线程的创建、上下文切换

线程的上下文切换概念:

多任务系统往往需要同时执行多道作业,而作业数往往都大于机器的CPU数,然而一个单核CPU同时只能执行一项任务,操作系统的设计者巧妙地利用了时间片轮转的方式。

1)时间片是CPU分配给各个线程的时间,时间片一般是几十毫秒。

2)CPU通过给每个线程分配CPU时间片,并且不停地切换线程来实现多线程。因为时间片非常短,所以感觉多个线程是在同时执行。

在当前任务执行的过程中,当操作系统分配的时间片用完后,会切换到下一个任务(即执行另外一个线程)。在切换之前,上一个任务的状态会被保存下来,下次切换回这个任务时,可以再加载这个任务的状态,任务从保存到再加载的过程就是一次上下文切换。

<
<think>嗯,用户想了解C++中的weak_ptr的用法以及它如何避免循环引用。首先,我需要回忆一下相关的知识。weak_ptr是C++11引入的智能指针之一,通常与shared_ptr配合使用。它的主要作用应该是解决shared_ptr之间的循环引用问题,避免内存泄漏。 根据引用中的内容,比如引用[2]提到weak_ptr不会增加引用计数,这应该是因为它只是观察对象,而不拥有所有权。那循环引用具体是怎么发生的呢?比如两个类互相持有对方的shared_ptr,这样它们的引用计数永远不会归零,导致无法释放内存。这时候就需要用weak_ptr来替换其中一个,打破循环。 接下来,用户可能想知道如何使用weak_ptr。根据引用[5],weak_ptr需要从shared_ptr构造,或者通过赋值来获取。使用时,可以通过lock()方法获取一个临时的shared_ptr来访问对象,这样可以确保在使用时对象仍然存在。例如,当需要访问weak_ptr指向的对象时,应该先调用lock()检查是否有效。 另外,weak_ptr的应用场景有哪些?比如观察者模式中,观察者持有被观察者的weak_ptr,这样不会影响被观察者的生命周期。或者在缓存中,当主对象不再需要缓存项时,缓存可以使用weak_ptr来保存,这样当主对象销毁时,缓存项可以自动释放。 然后需要解释如何用weak_ptr解决循环引用的问题。比如,在父子类结构中,如果父节点持有子节点的shared_ptr,而子节点又持有父节点的shared_ptr,就会形成循环。这时候可以将子节点中的父节点指针改为weak_ptr,这样父节点的引用计数不会被子节点增加,当外部释放父节点时,其引用计数会归零,从而正确释放内存。 可能还需要注意weak_ptr本身不管理生命周期,因此在使用前必须检查有效性。lock()方法返回的shared_ptr如果为空,说明对象已经被释放,这时候就不能再访问了。 总结一下,回答应该包括weak_ptr的基本用法、如何解决循环引用、使用时的注意事项以及适用场景。同时根据用户要求,生成相关的问题,比如性能影响、多线程安全、与原始指针的比较等。</think>### C++ weak_ptr 使用方法及避免循环引用 #### 1. weak_ptr 的基本用法 `std::weak_ptr` 是 C++11 引入的智能指针,需与 `std::shared_ptr` 配合使用。它不直接管理对象的生命周期,而是“观察”由 `shared_ptr` 管理的对象,且**不会增加引用计数**。 - **构造方法**: ```cpp auto shared = std::make_shared<int>(42); std::weak_ptr<int> weak(shared); // 通过 shared_ptr 构造 weak_ptr ``` - **访问对象**: 使用 `lock()` 方法获取一个临时的 `shared_ptr`,若原对象已被释放,则返回空指针: ```cpp if (auto temp = weak.lock()) { std::cout << *temp << std::endl; // 安全访问 } else { std::cout << "对象已释放" << std::endl; } ``` #### 2. 如何解决循环引用问题? 循环引用是指两个或多个对象通过 `shared_ptr` 互相持有对方,导致引用计数无法归零,内存无法释放。 **示例场景**: ```cpp class B; class A { public: std::shared_ptr<B> b_ptr; }; class B { public: std::shared_ptr<A> a_ptr; }; auto a = std::make_shared<A>(); auto b = std::make_shared<B>(); a->b_ptr = b; b->a_ptr = a; // 循环引用,a 和 b 的引用计数均为 2,无法释放 ``` **解决方法**: 将其中一个 `shared_ptr` 替换为 `weak_ptr`,打破循环: ```cpp class B; class A { public: std::shared_ptr<B> b_ptr; }; class B { public: std::weak_ptr<A> a_ptr; // 改为 weak_ptr }; auto a = std::make_shared<A>(); auto b = std::make_shared<B>(); a->b_ptr = b; b->a_ptr = a; // a 的引用计数保持为 1,b 的引用计数为 2 ``` 当外部 `a` 释放时,其引用计数归零,`A` 对象被销毁,随后 `B` 对象引用计数减为 1,最终释放[^3][^4]。 #### 3. 注意事项 - **生命周期管理**:`weak_ptr` 不拥有对象,需通过 `lock()` 检查对象有效性。 - **线程安全**:`lock()` 在多线程环境中需配合同步机制使用。 - **性能开销**:`weak_ptr` 的 `lock()` 操作涉及原子操作,但通常可忽略不计[^5]。 #### 4. 适用场景 - **观察者模式**:观察者持有被观察者的 `weak_ptr`,避免影响被观察者的生命周期。 - **缓存系统**:缓存项使用 `weak_ptr`,当主对象释放时缓存自动失效。 - **解决循环引用**:如树形结构、双向链表等[^2][^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值