代码之家  ›  专栏  ›  技术社区  ›  SpeakX

多线程上下文中的弱指针未过期时更新

  •  0
  • SpeakX  · 技术社区  · 1 年前

    这个 客户 保存指向某个值的共享指针,例如 double ,和 服务器 通过保持 weak_pointer ,服务器检查 weak_pointer 已过期,如果未过期,则从安全向量中删除。我怀疑这不会起作用 shared_ptr 可以从客户端销毁 之间 我的阅读 expired() (我认为这是线程安全的,因为我认为它指的是的原子计数器 shared_ptr ) 我更新的值。有办法结账吗 expired() 和一个 lambda 函数在值被破坏之前更新它,好吗?我的意思是:

    struct server
    {
    public:
        static void subscribe(const std::shared_ptr<double>& d)
        {
            m_values.safe_push_back(d); //safely pushes back in the vector
        }
    
        void update()
        {
            auto updater = [](std::weak_ptr<double>& wd)
            {
                if(wd.expired()) wd = nullptr;
                else *wd += 2.0; //This is not thread safe I guess?
            };
            m_values.safe_remove_all(nullptr);
            m_values.safe_visit(updater);
        };
    
    private:
        static safe_vector<std::weak_ptr<double>> m_values;
    }
    
    struct client
    {
        void subcribe_sleep_print(const double random_seconds)
        {
            std::shared_ptr<double> d = std::make_shared<double>(0.0); //for the sake of the example assume we create subscribe before sleep
            server::subscribe(d);
            sleep_for_seconds(random_seconds); //sleeps for some random time in seconds.
            std::cout << *d << std::endl;
        }
    }
    

    想象 server::update client::subcribe_sleep_print 正在不同的线程上运行。这是不安全的,因为 shared_ptr 可能在服务器写入时被破坏?有没有一种方法可以在没有用户定义的原子或互斥体的情况下做到这一点?我不介意它是否在后台使用,只要我自己不添加它们(原子/互斥),因为我知道共享指针已经使用了原子计数器。

    编辑 :我在程序中没有使用double,我使用的是线程安全的对象:)所以操作+=可以被认为是线程安全。很抱歉

    1 回复  |  直到 1 年前
        1
  •  0
  •   Ext3h    1 年前
            else *wd += 2.0; //This is not thread safe I guess?
    

    不,这不是线程安全的。您正面临一种竞争条件,指针可能会在您检查过期后立即过期。在这种情况下,您现在已取消引用 nullptr .

    因此,方法是明确使用 std::weak_ptr<T>::lock() 尝试获取关联的共享指针(也是 expired() 内部完成),并且只有当它获得了有效的指针时,您才能进行读取或写入访问。

    如果您没有获得有效的共享指针,请将其视为 expired() 返回了false。

       m_values.safe_remove_all(nullptr);
       m_values.safe_visit(updater);
    

    我确信你的订单搞错了。您本来打算先更新,然后删除 nullptr 您的更新回调已产生。