C++中的线程安全删除

Posted

技术标签:

【中文标题】C++中的线程安全删除【英文标题】:Threadsafe delete in C++ 【发布时间】:2020-07-12 08:21:32 【问题描述】:

我的问题不是如何从头开始制作这个功能,而是我的实现是否可以使用。

我有下一堂课:

    class PointerStorage
    
    private:
        static std::mutex m;
        static std::unordered_set<void*> pointers;
    public:
        static inline void add(void* ptr)
            m.lock();
            PointerStorage::pointers.insert(ptr);
            m.unlock();
        
        static inline bool tryRemove(void* ptr) 
            std::lock_guard<std::mutex> g(m);
            return PointerStorage::pointers.erase(ptr);
        
        static inline bool hasPtr(void* ptr) 
            std::lock_guard<std::mutex> g(m);
            return PointerStorage::pointers.find(ptr) != PointerStorage::pointers.end();
        
    ;
    class Deletable
    
    public:
        void* operator new(size_t size) 
            void* p = malloc(size);
            PointerStorage::add(p);
            return p;
        
        void operator delete(void* ptr) 
            if (PointerStorage::tryRemove(ptr)) 
                free(ptr);
            
        
    ;

我的想法是在必要时从Deletable 继承并使用运算符

但我想知道这种情况是否可能:

我在变量OBJ中创建了一个对象,删除它。 我在另一个变量中创建了一个新对象,它的地址与OBJ 中的地址相同,OBJ 上的删除操作会影响我的新对象。

【问题讨论】:

为什么需要这个?对线程之间共享的对象的任何变异操作都需要锁定。为什么要单独删除?无论如何,我认为您描述的情况没有问题。新的对象地址可能与旧的已删除对象地址相同,这有什么影响? 但是我有一个事件系统,它可能在不同的侦听器中拥有相同的指针(甚至是相同的侦听器,因为它们可以在不同的线程中执行)。 我不知道你为什么认为这很重要。当一个对象还活着时,只有那个对象有这个地址。当一个对象被删除时,该地址的对象就不再存在,它可以被重用。如果您在程序周围保留可能已删除对象的地址,那么您就会遇到与线程无关的问题。 【参考方案1】:

通常你不应该假设用于旧对象的指针不会被新对象重用。

有时你会想做出这样的假设。

对于多线程程序,当你想确保指针不被重复使用时,这种情况是 ABA 问题。简单来说,这个问题发生在旧指针和新指针比较交换时,但不能确定旧指针确实是旧指针。

一种解决方案基本上是将旧指针保留一段时间,这称为Hazard pointer。

另一种是不仅仅依赖指针值,还依赖一个计数器(虽然计数器仍然可以回绕,但是失败的概率更低)。

一般来说,最好的解决方案是不要遇到需要假设旧指针未被重用的情况。 ABA 问题 是特殊情况,但是,特定于无锁,当避免可能意味着根本没有无锁算法时。

【讨论】:

以上是关于C++中的线程安全删除的主要内容,如果未能解决你的问题,请参考以下文章

C++ 中的线程安全单例实现

c++ 中的多线程线程安全动画建议

C++线程安全双向链表

线程安全堆栈 C++ 中的潜在死锁

C++ 中的标准输出流是线程安全的(cout、cerr、clog)吗?

C++ 中的异步线程安全日志记录(无互斥体)