【问题标题】:How to safely destroy an object, that is frequently accessed by two different threads?如何安全地销毁一个经常被两个不同线程访问的对象?
【发布时间】:2013-09-26 16:03:48
【问题描述】:

我目前遇到的问题是必须释放一个经常被两个不同线程访问的对象(实例)。对我来说,两个线程中的哪一个正在破坏实例并不重要,但我更喜欢那个也创建它的线程,尽管我认为这根本不重要。

所以在一个场景中,应该销毁对象的线程检测到它应该被删除,并且在调用析构函数时,另一个线程正在访问该对象的成员(函数),可能是某种运行时会发生错误。

我对此主题进行了一些研究,但我只能弄清楚人们说:“为什么要删除仍然需要存在的对象”。但在我的情况下,在一个线程正在执行的一段代码决定销毁它之后,它应该不再需要了。

我会很感激您的回答,例如对涵盖此主题的好书或文章的提示,但请随时写下您将如何解决此问题。

【问题讨论】:

  • 删除与任何其他同步问题没有任何不同。使用互斥锁或其他东西。
  • 不使用smart pointers?
  • 等到两个线程都完成了吗?
  • 使用引用计数的智能指针来保存对对象的引用。我建议 shared_ptr,至少 Boost 是线程安全的。
  • @UlrichEckhardt std::shared_ptr (C++11) 也是线程安全的。

标签: c++ multithreading simultaneous ownership destruction


【解决方案1】:

您需要双重间接来管理对数据的并发访问:

class Object {
    public;
    class Data {
        int get_value() const;
        void set_value(int);
    };

    class Guard {
        public:
        Guard(Nutex& mutex, Data* data) 
        :   data(data)
        {
            if( ! data) throw std::runtime_error("No Data");
            mutex.lock();
        }

        ~Guard() 
        {
            mutex.unlock();
        }

        Data& data() { return *m_data; }

        private:
        Data* data;
    };

    class ConstGuard {
        public:
        ConstGuard(Mutex& mutex, const Data* data) 
        :   data(data)
        {
            if( ! data) throw std::runtime_error("No Data");
            mutex.lock();
        }

        ~ConstGuard() 
        {
            mutex.unlock();
        }

        const Data& data() const { return *m_data; }

        private:
        const Data* data;
    };

    private:
    friend std::shared_ptr<Object> make_object(const Data&);
    Object(const Data& data)
    :   data(new Data(data))
    {}

    public:
    ~Object()
    {
        delete data;
    }

    /// Self destruction.
    void dispose() {
        Guard guard(get());
        delete data;
        data = 0;        
    }

    // For multiple operations use a Guard.
    Guard get() { return Guard(mutex, data); }
    ConstGuard get() const { return ConstGuard(mutex, data); }

    // For single operations you may provide convenience functions.
    int get_value() const { return ConstGuard(mutex, data).data().get_value(); }
    void set_value(int value) { Guard(mutex, data).data().set_value(value); }

    private:
    mutable Mutex mutex;
    data* data;
};

std::shared_ptr<Object> make_object(const Object::Data& data) {
    return std::make_shared<Object>(data);
}

(注:以上代码只是一个草图,我没有编译过)

说来话长。简短的是 [20.7.2.5] shared_ptr 原子访问:

Concurrent access to a shared_ptr object from multiple threads does not
introduce a data race if the access is done exclusively via the functions
in this section and the instance is passed as their first argument.
  • shared_ptr atomic_load(const shared_ptr* p);
  • void atomic_store(shared_ptr* p, shared_ptr r);
  • ... 但我对这些功能并不熟悉。

【讨论】:

    【解决方案2】:

    应该销毁对象的线程应该使用std::weak_ptr 来持有它,除非它正在积极使用它。在此期间,weak_ptr 可以升级为std::shared_ptr

    现在应该销毁它的线程可以在它认为合适的时候保留它的shared_ptr,然后丢弃那个shared_ptr。如果另一个线程只有一个weak_ptr(没有主动使用它),该对象就会消失。如果另一个线程也有shared_ptr,它会一直持有该对象,直到操作完成。

    【讨论】:

      猜你喜欢
      • 2012-02-17
      • 1970-01-01
      • 2013-01-15
      • 1970-01-01
      • 2017-06-29
      • 2012-02-06
      • 2015-05-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多