1. 共享指针
#include <atomic>
#include <iostream>template <typename T> class sharedptr {
private:T *ptr;std::atomic<size_t> *count;public:sharedptr(T *p) : ptr(p), count(new std::atomic<size_t>(1)) {}sharedptr(const sharedptr &other) : ptr(other.ptr), count(other.count) {(*count)++;}sharedptr(sharedptr &&other) : ptr(other.ptr), count(other.count) {other.ptr = nullptr;other.count = nullptr;}// 拷贝赋值运算符// 何时调用?当用一个 已存在的对象(左值)// 赋值给另一个已存在的对象时: Myclass a,b;sharedptr &operator=(const sharedptr &other) {if (this != &other) {release();ptr = other.ptr;count = other.count;(*count)++;}return *this;}// 移动赋值运算符sharedptr &operator=(sharedptr &&other) {if (this != &other) {ptr = other.ptr;count = other.count;other.ptr = nullptr;other.count = nullptr;}return *this;}void release() {if (count != nullptr && (--(*count)) == 0) {delete ptr;delete count;}}~sharedptr() { release(); }T *operator->() { return ptr; }T &operator*() { return *ptr; }
};
int main() {}
1. 快速记忆
-
双成员结构:
T* ptr
管理资源atomic<size_t>* count
保证线程安全
-
五大特殊函数:
// 1. 构造 explicit Shared_Ptr(T* p = nullptr)// 2. 拷贝构造 Shared_Ptr(const Shared_Ptr&)// 3. 移动构造(noexcept优化) Shared_Ptr(Shared_Ptr&&) noexcept// 4. 拷贝赋值(自检+释放旧值:等号左) operator=(const Shared_Ptr&)// 5. 移动赋值(自检+释放旧值:等号右) operator=(Shared_Ptr&&) noexcept
-
建议手写时按以下顺序实现
-
成员变量
- 构造函数
- 拷贝构造/赋值
- 移动构造/赋值
- 析构函数
- 操作符重载
- 辅助方法
-
2. 多线程交替打印
代码:
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>int m;
std::mutex mtx;
std::condition_variable cv;
void print(int target) {for (int i = 0; i < 10; i++) {// 这行代码相当重要std::unique_lock<std::mutex> lock(mtx);// 条件谓词不接受参数cv.wait(lock, [&]() { return m == target; });std::cout << m << std::endl;m = (m + 1) % 3;cv.notify_all();}
}
int main() {m = 0;std::thread t1(print, 0);std::thread t2(print, 1);std::thread t3(print, 2);t1.join();t2.join();t3.join();
}
1. std::unique_lock<std::mutex>对象lock和std::mutex对象mtx是什么关系?
std::unique_lock<std::mutex>
是一个管理互斥量(std::mutex
)的 RAII(资源获取即初始化)包装器。两者的关系如下:
-
绑定关系:当你创建一个
std::unique_lock<std::mutex>
对象时,通常会将一个std::mutex
对象(比如mtx
)传递给它。此时,unique_lock
会自动尝试锁定这个互斥量。 -
自动管理:
unique_lock
在其生命周期内拥有并管理这个mtx
的锁定状态。当unique_lock
对象销毁时(例如超出作用域),它会自动解锁mtx
,避免手动解锁带来的错误风险。 -
灵活性:与
std::lock_guard
相比,unique_lock
提供了更多的灵活性,比如能够延迟锁定、手动解锁或重新锁定,以及支持条件变量等待时的锁管理。 -
个人理解:所谓mutex(mutual exclusion),本身仅是声明了一个互斥量,此时并不包含锁的意义
2. 简述一下cv.wait和notify做了什么?
-
cv.wait
-
等待与解锁:调用
cv.wait(lock, predicate)
时,线程会进入等待状态,同时自动释放传入的std::unique_lock
持有的互斥量。这使得其他线程可以获得该互斥量,从而修改共享数据。 -
阻塞与恢复:当等待的条件(通常由传入的 lambda 表达式 predicate 检查)不满足时,线程会一直阻塞。待其他线程调用
notify
方法后,等待线程会被唤醒并重新尝试获取锁,随后检查条件,直到条件满足为止。
-
-
notify
-
唤醒等待线程:
cv.notify_one()
会唤醒一个正等待此条件变量的线程,而cv.notify_all()
则会唤醒所有等待的线程。 -
同步机制:当某个线程修改了共享数据并满足了等待线程所期望的条件后,它会调用
notify
方法通知等待线程,促使它们从阻塞状态中恢复,继续执行。
-
3. LRU缓存
class LRUCache {
private:int capacity;list<pair<int,int>> mylist;unordered_map<int,list<pair<int,int>>::iterator> umap;
public:LRUCache(int capacity):capacity(capacity) {}int get(int key) {if(umap.find(key)==umap.end()) {return -1;}int value=umap[key]->second;mylist.erase(umap[key]);mylist.push_front({key,value});umap[key]=mylist.begin();return value;}void put(int key, int value) {if(umap.find(key)!=umap.end()){mylist.erase(umap[key]);mylist.push_front({key,value});umap[key]=mylist.begin();} else{if(umap.size()==capacity){// 不能直接删除end()// umap.erase(mylist.end());int old_key=mylist.back().first;umap.erase(old_key);mylist.pop_back();}mylist.push_front({key,value});umap[key]=mylist.begin();}}
};