呵呵哒( ̄▽ ̄)" 2024-02-04 16:26 采纳率: 100%
浏览 5
已结题

如何实现智能指针安全和验证呢?

这是我改写的share_ptr的加锁版本,
问题一:不知道怎么验证是否线程安全
问题二:weak_ptr需不需要也加锁呢?如何验证线程安全呢?
share_ptr.cpp


#pragma once 
#include <iostream>
#include <string>
#include <mutex>
using namespace std;
namespace heheda {
    namespace utility {
        template<class T>
        class SharePtr {
            template<class Y> friend class WeakPtr;
        public:
            SharePtr() : m_data(nullptr), m_count(nullptr),m_mutex(nullptr) {}
            SharePtr(T* data) {
                if(data != nullptr) {
                    m_data = data;
                    m_count = new int(1);
                    m_mutex = new mutex;
                }
            }    
            // 拷贝构造函数
            SharePtr(const SharePtr<T>& other) 
                : m_data(other.m_data)
                , m_count(other.m_count)
                , m_mutex(other.m_mutex) 
            {     
                if(m_data != nullptr) addRefCount();// 增加引用计数
            }   
            // 移动构造函数
            SharePtr(SharePtr<T>&& other) noexcept
                : m_data(other.m_data)
                , m_count(other.m_count)
                , m_mutex(other.m_mutex) {  // move
                other.m_data = nullptr;
                other.m_count = nullptr;
                other.m_mutex = nullptr;
            }
            // 析构函数
            ~SharePtr() {
                if(m_data != nullptr) {
                    release();
                }
            }
            void release() {
                bool deleteflag = false;
                m_mutex->lock();
                if(--(*m_count) == 0) {
                    cout << "delete:" << m_data << endl;
                    // 释放资源
                    delete m_data;m_data = nullptr;
                    delete m_count;m_count = nullptr;
                    deleteflag = true;
                }
                m_mutex->unlock();
                //表示m_ptr 和 m_pRefCount资源已经被释放,此时需要释放锁,否则产生资源泄漏
                if(deleteflag) delete m_mutex;
            }

            T*get() const { return m_data; }
            T*getCount() const { return m_count; }
            void reset(T* data = nullptr) {
                if(m_data == data) return;
                if(m_data == nullptr) {
                    if(data!=nullptr) {
                        m_data = data;
                        m_mutex->lock();
                        m_count = new int(1);
                        m_mutex->unlock();
                    }
                    return;
                }
                m_mutex->lock();
                (*m_count)--;
                if(*m_count <= 0) {
                    delete m_data;
                    m_data = nullptr;
                    delete m_count;
                    m_count = nullptr;
                }
                m_data = data;
                if(data != nullptr) {
                    m_count = new int(1);
                }
                m_mutex->unlock();
            }
            int use_count() const {
                if(m_data == nullptr) return 0;
                return *m_count;
            }
            bool unique() const {
                if(m_data == nullptr) return false;
                return *m_count == 1;
            }
            void swap(SharePtr<T>& other) {
                auto data = other.m_data;
                auto count = other.m_count;
                auto m_mutex = other.m_mutex;
                other.m_data = m_data;
                other.m_count = m_count;
                other.m_mutex = m_mutex;
                m_data = data;
                m_count = count;
                other.m_mutex = m_mutex;
            }
            T* operator->() const { return m_data; }
            T& operator*() const { return *m_data; }
            explicit operator bool() const noexcept { return m_data != nullptr; }
            SharePtr& operator=(const SharePtr<T>& other) {
                if(this == &other) return *this;
                m_data = other.m_data;
                m_count = other.m_count;
                m_mutex = other.m_mutex;
                addRefCount();// 增加引用计数
                return *this;
            }
            // 移动赋值
            SharePtr& operator=(SharePtr<T>&& other) noexcept {
                if(this == &other) return *this;
                m_data = other.m_data;
                m_count = other.m_count;
                m_mutex = other.m_mutex;
                other.m_data = nullptr;
                other.m_count = nullptr;
                other.m_mutex = nullptr;
                return *this;
            }
            // 增加引用计数
            void addRefCount() {
                m_mutex->lock();
                ++(*m_count);
                m_mutex->unlock();
            }
        private:
            T* m_data;    // 指向管理资源的指针
            int* m_count; // 指向引用计数的指针
            mutex *m_mutex;   // 互斥锁,处理线程安全
        };
    }
}

weak_ptr.cpp

#pragma once
#include "shared_ptr.h"
// #include "thread_shared_ptr.h"
namespace heheda{
    // weak_ptr和shared_ptr最大的区别:
    // shared_ptr是一个共享智能指针,每多一个智能指针,它的引用计数就加1
    // 多个weak_ptr可以指向同一个对象,但是它并不增加引用计数
    // shared_ptr会管理它的生命周期,但每多一个引用,它的引用计数就会加1
    // 当某一个shared_ptr销毁的时候,引用计数就会减1,如果说减到0的话,
    // 那这个对象就会被释放掉.
    // weak_ptr它不会释放对象,它也不会增加这个引用计数
    namespace utility {
        template<class T>
        class WeakPtr{
        public:
            WeakPtr():m_data(nullptr),m_count(nullptr){}
            // WeakPtr(const SharePtr<T>& sp):m_data(sp.get()),m_count(sp.getCount()){}    
            WeakPtr(const SharePtr<T>& sp):m_data(sp.m_data),m_count(sp.m_count){}    
            // 拷贝构造函数
            WeakPtr(const WeakPtr<T>& other):m_data(other.m_data),m_count(other.m_count){}  
            // 移动构造函数
            WeakPtr(WeakPtr<T> && other):m_data(other.m_data),m_count(other.m_count){
                other.m_data = nullptr;
                other.m_count = nullptr;
            }
            ~WeakPtr(){
                m_data = nullptr;
                m_count = nullptr;
            }
            void reset(){
                m_data = nullptr;
                m_count = nullptr;
            }
            bool expired() const{
                return !m_count || (*m_count)<=0;
            }
            SharePtr<T> lock() const{
                if(expired()) {
                    return SharePtr<T>();
                }
                SharePtr<T> sp;// 临时对象,被析构就会--
                sp.m_data = m_data;
                sp.m_count = m_count;
                if(m_count != nullptr) {
                    (*m_count)++;
                }
                return sp;
            }
            void swap(WeakPtr<T>& other) {
                auto data = other.m_data;
                auto count = other.m_count;
                other.m_data = m_data;
                other.m_count = m_count;
                m_data = data;
                m_count = count;
            }

            int use_count() const{
                if(m_data == nullptr) return 0;
                return *m_count;
            }

            WeakPtr& operator=(const SharePtr<T>& sp) {
                m_data = sp.m_data;
                m_count = sp.m_count;
                return *this;
            }

            WeakPtr& operator=(const WeakPtr<T>& other) {
                if(this == &other) return *this;
                m_data = other.m_data;
                m_count = other.m_count;
                return *this;
            }
            // 移动赋值
            WeakPtr & operator=(WeakPtr<T> && other) {
                if(this == &other) return *this;
                m_data = other.m_data;
                m_count = other.m_count;
                other.m_data = nullptr;
                other.m_count = nullptr;
                return *this;
            }
        private:
            T* m_data;
            int* m_count;
        };
    }
}

  • 写回答

3条回答 默认 最新

  • 专家-郭老师 Java领域新星创作者 2024-02-04 16:28
    关注

    参考通义千问和郭老师的小迷弟雅思莫了-编写提供,主要是为了帮助您快速解决问题,如下回答,您可以参考下,如有问题,评论区留言。

    您的实现中确实添加了互斥锁来保证线程安全,这是一个好的开始。要验证智能指针是否线程安全,可以设计和执行一些多线程测试用例,例如:

    1. 在多个线程中同时创建、复制、移动、释放 SharePtr 对象,并确保资源正确分配和释放,引用计数正确增减。
    2. 使用多个线程同时访问并修改共享对象,确保在读写操作时不会出现竞态条件。

    对于问题一:为了进一步提高线程安全性,您需要确保在所有对引用计数和指针进行修改的地方(如构造函数、析构函数、reset()addRefCount()release() 等)都正确使用互斥锁进行保护。另外,考虑异常安全,确保即使在抛出异常的情况下也能正确释放资源。

    对于问题二:weak_ptr 也需要线程安全。当 weak_ptr 访问其内部的 shared_ptr 数据时,必须保证线程安全。通常的做法是,在 weak_ptr 的构造、赋值、重置以及 lock() 方法中锁定 shared_ptr 的互斥锁。但请注意,weak_ptr 内部并不需要自己的引用计数,它只是存储指向 shared_ptr 引用计数器的指针。

    关于如何验证 weak_ptr 是否线程安全,您可以编写类似的并发测试用例,模拟多个线程同时创建、复制、销毁 weak_ptr 并调用 lock() 转换为 shared_ptr 的情况,观察是否会出现数据竞争或不一致的状态。

    以下是针对 weak_ptr 需要注意的部分代码调整:

    template<class T>
    class WeakPtr {
    public:
        // ...
        WeakPtr(const SharePtr<T>& sp) : m_data(sp.m_data), m_count(sp.m_count) {
            if (m_count != nullptr) {
                m_mutex = sp.m_mutex;
                m_mutex->lock();
                (*m_count)++;
                m_mutex->unlock();
            }
        }
    
        ~WeakPtr() {
            if (m_count && m_mutex) {
                m_mutex->lock();
                (*m_count)--;
                m_mutex->unlock();
            }
            // ...
        }
    
        void reset() {
            if (m_count && m_mutex) {
                m_mutex->lock();
                (*m_count)--;
                m_data = nullptr;
                m_count = nullptr;
                m_mutex->unlock();
            }
            // ...
        }
    
        SharePtr<T> lock() const {
            if (expired()) {
                return SharePtr<T>();
            }
            SharePtr<T> sp;
            sp.m_data = m_data;
            sp.m_count = m_count;
            sp.m_mutex = m_mutex; // 这里也需要传递mutex给临时的shared_ptr
            m_mutex->lock();
            (*m_count)++;
            m_mutex->unlock();
            return sp;
        }
    
        // ...
    };
    

    请根据实际情况调整和完善上述代码,同时补充缺少的析构函数等部分。在实际编程过程中,推荐使用 STL 中提供的 std::shared_ptr 和 std::weak_ptr,它们已经过充分优化和测试,具备良好的线程安全性。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

问题事件

  • 系统已结题 2月19日
  • 已采纳回答 2月11日
  • 创建了问题 2月4日

悬赏问题

  • ¥15 python的qt5界面
  • ¥15 无线电能传输系统MATLAB仿真问题
  • ¥50 如何用脚本实现输入法的热键设置
  • ¥20 我想使用一些网络协议或者部分协议也行,主要想实现类似于traceroute的一定步长内的路由拓扑功能
  • ¥30 深度学习,前后端连接
  • ¥15 孟德尔随机化结果不一致
  • ¥15 apm2.8飞控罗盘bad health,加速度计校准失败
  • ¥15 求解O-S方程的特征值问题给出边界层布拉休斯平行流的中性曲线
  • ¥15 谁有desed数据集呀
  • ¥20 手写数字识别运行c仿真时,程序报错错误代码sim211-100