C++手撕共享指针、多线程交替、LRU缓存

打印 上一主题 下一主题

主题 1750|帖子 1750|积分 5250

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
 1. 共享指针

  1. #include <atomic>
  2. #include <iostream>
  3. template <typename T> class sharedptr {
  4. private:
  5.   T *ptr;
  6.   std::atomic<size_t> *count;
  7. public:
  8.   sharedptr(T *p) : ptr(p), count(new std::atomic<size_t>(1)) {}
  9.   sharedptr(const sharedptr &other) : ptr(other.ptr), count(other.count) {
  10.     (*count)++;
  11.   }
  12.   sharedptr(sharedptr &&other) : ptr(other.ptr), count(other.count) {
  13.     other.ptr = nullptr;
  14.     other.count = nullptr;
  15.   }
  16.   // 拷贝赋值运算符
  17.   // 何时调用?当用一个 ​已存在的对象(左值)​
  18.   // 赋值给另一个已存在的对象时: Myclass a,b;
  19.   sharedptr &operator=(const sharedptr &other) {
  20.     if (this != &other) {
  21.       release();
  22.       ptr = other.ptr;
  23.       count = other.count;
  24.       (*count)++;
  25.     }
  26.     return *this;
  27.   }
  28.   // 移动赋值运算符
  29.   sharedptr &operator=(sharedptr &&other) {
  30.     if (this != &other) {
  31.       ptr = other.ptr;
  32.       count = other.count;
  33.       other.ptr = nullptr;
  34.       other.count = nullptr;
  35.     }
  36.     return *this;
  37.   }
  38.   void release() {
  39.     if (count != nullptr && (--(*count)) == 0) {
  40.       delete ptr;
  41.       delete count;
  42.     }
  43.   }
  44.   ~sharedptr() { release(); }
  45.   T *operator->() { return ptr; }
  46.   T &operator*() { return *ptr; }
  47. };
  48. int main() {}
复制代码
1. 快速影象


  • 双成员布局

    • T* ptr管理资源
    • atomic<size_t>* count保证线程安全

  • 五大特殊函数
    1. // 1. 构造
    2. explicit Shared_Ptr(T* p = nullptr)
    3. // 2. 拷贝构造
    4. Shared_Ptr(const Shared_Ptr&)
    5. // 3. 移动构造(noexcept优化)
    6. Shared_Ptr(Shared_Ptr&&) noexcept
    7. // 4. 拷贝赋值(自检+释放旧值:等号左)
    8. operator=(const Shared_Ptr&)
    9. // 5. 移动赋值(自检+释放旧值:等号右)
    10. operator=(Shared_Ptr&&) noexcept
    复制代码
  • 发起手写时按以下顺序实现

    • 成员变量
    • 构造函数
    • 拷贝构造/赋值
    • 移动构造/赋值
    • 析构函数
    • 操作符重载
    • 辅助方法

2. 多线程交替打印

代码:

  1. #include <condition_variable>
  2. #include <iostream>
  3. #include <mutex>
  4. #include <thread>
  5. int m;
  6. std::mutex mtx;
  7. std::condition_variable cv;
  8. void print(int target) {
  9.   for (int i = 0; i < 10; i++) {
  10.     // 这行代码相当重要
  11.     std::unique_lock<std::mutex> lock(mtx);
  12.     // 条件谓词不接受参数
  13.     cv.wait(lock, [&]() { return m == target; });
  14.     std::cout << m << std::endl;
  15.     m = (m + 1) % 3;
  16.     cv.notify_all();
  17.   }
  18. }
  19. int main() {
  20.   m = 0;
  21.   std::thread t1(print, 0);
  22.   std::thread t2(print, 1);
  23.   std::thread t3(print, 2);
  24.   t1.join();
  25.   t2.join();
  26.   t3.join();
  27. }
复制代码
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缓存

  1. class LRUCache {
  2. private:
  3.     int capacity;
  4.     list<pair<int,int>> mylist;
  5.     unordered_map<int,list<pair<int,int>>::iterator> umap;
  6. public:
  7.     LRUCache(int capacity):capacity(capacity) {
  8.     }
  9.    
  10.     int get(int key) {
  11.         if(umap.find(key)==umap.end())  {return -1;}
  12.         int value=umap[key]->second;
  13.         mylist.erase(umap[key]);
  14.         mylist.push_front({key,value});
  15.         umap[key]=mylist.begin();
  16.         return value;
  17.     }
  18.    
  19.     void put(int key, int value) {
  20.         if(umap.find(key)!=umap.end()){
  21.             mylist.erase(umap[key]);
  22.             mylist.push_front({key,value});
  23.             umap[key]=mylist.begin();
  24.         }   else{
  25.                 if(umap.size()==capacity){
  26.                     //  不能直接删除end()
  27.                     // umap.erase(mylist.end());
  28.                     int old_key=mylist.back().first;
  29.                     umap.erase(old_key);
  30.                     mylist.pop_back();
  31.                 }
  32.                 mylist.push_front({key,value});
  33.                 umap[key]=mylist.begin();
  34.         }
  35.     }
  36. };  
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

伤心客

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表