微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c – 锁定一个shared_ptr

我有一个共享对象,需要发送到一个系统API,并在以后解压缩.系统API只接收void *.我不能使用shared_ptr :: get(),因为它不会增加引用计数,并且可以在从系统API提取之前被其他线程释放.发送一个新的shared_ptr *将会起作用,但需要额外的堆分配.

一种方法是让来自enable_shared_from_this的对象.但是,由于此类模板仅拥有weak_ptr,因此保持对象不被释放是不够的.

所以我的解决方案如下所示:

class MyClass:public enable_shared_from_this<MyClass> {
private:
    shared_ptr<MyClass> m_this;
public:
    void *lock(){
        m_this=shared_from_this();
        return this;
    }
    static shared_ptr<MyClass> unlock(void *p){
        auto pthis = static_cast<MyClass *>(p);
        return move(pthis->m_this);
    }
/* ... */
}

/* ... */
autp pobj = make_shared<MyObject>(...);
/* ... */
system_api_send_obj(pobj->lock());
/* ... */
auto punlocked = MyClass::unlock(system_api_reveive_obj());

有没有更简单的方法呢?

这个解决方案的缺点:

>它需要一个额外的shared_ptr< MyClass>在MyClass对象布局中,除了在基类enable_shared_from_this中的weak_ptr之外.
>正如我在评论中提到的,同时访问lock()和unlock()是不安全的.
最糟糕的是,这个解决方案只能在调用unlock()之前支持一次lock().如果同一个对象用于多个系统API调用,则必须执行额外的引用计数.

如果我们有另一个enable_lockable_shared_from_this类,那将是greate:

class MyClass:public enable_lockable_shared_from_this<MyClass> {
/* ... */
}

/* ... */
autp pobj = make_shared<MyObject>(...);
/* ... */
system_api_send_obj(pobj.lock());
/* ... */
auto punlocked = unlock_shared<MyClass>(system_api_reveive_obj());

而enable_lockable_shared_from_this的实现类似于enable_shared_from_this,唯一的区别是它实现了lock()和一个帮助函数unlock_shared.调用这些函数只显式地增加和减少use_count().这将是理想的解决方案,因为:

>消除额外的空间成本
>它重用shared_ptr现有的设施来保证并发安全.
>这个解决方案的最好的办法是它可以无缝地支持多个lock()调用.

然而,唯一最大的缺点是:目前不可用!

更新:

这个问题的至少两个答案涉及一个指针的容器.请将这些解决方案与以下内容进行比较:

class MyClass:public enable_shared_from_this<MyClass> {
private:
    shared_ptr<MyClass> m_this;
    mutex this_lock; //not necessory for single threading environment
    int lock_count;
public:
    void *lock(){
        lock_guard lck(this_lock); //not necessory for single threading environment
        if(!lock_count==0)
            m_this=shared_from_this();
        return this;
    }
    static shared_ptr<MyClass> unlock(void *p){
        lock_guard lck(this_lock); //not necessory for single threading environment
        auto pthis = static_cast<MyClass *>(p);
        if(--lock_count>0)
            return pthis->m_this;
        else {
            lock_count=0;
            return move(pthis->m_this); //returns nullptr if not prevIoUsly locked
        }
    }
/* ... */
}

/* ... */
autp pobj = make_shared<MyObject>(...);
/* ... */
system_api_send_obj(pobj->lock());
/* ... */
auto punlocked = MyClass::unlock(system_api_reveive_obj());

这绝对是O(1)vs O(n)(空间;时间是O(log(n))或类似的,但绝对大于O(1))游戏.

解决方法

我现在有一个想法如下:
template<typename T>
struct locker_helper{
    typedef shared_ptr<T> p_t;
    typedef typename aligned_storage<sizeof(p_t),alignment_of<p_t>::value>::type s_t;
};
template<typename T> void lock_shared(const shared_ptr<T> &p){
    typename locker_helper<T>::s_t value;
    new (&value)shared_ptr<T>(p);
}
template<typename T> void unlock_shared(const shared_ptr<T> &p){
    typename locker_helper<T>::s_t value
        = *reinterpret_cast<const typename locker_helper<T>::s_t *const>(&p);
    reinterpret_cast<shared_ptr<T> *>(&value)->~shared_ptr<T>();
}


template<typename T>
void print_use_count(string s,const shared_ptr<T> &p){
    cout<<s<<p.use_count()<<endl;
}

int main(int argc,char **argv){
    auto pi = make_shared<int>(10);
    auto s = "pi use_count()=";
    print_use_count(s,pi); //pi use_count()=1
    lock_shared(pi);
    print_use_count(s,pi);//pi use_count()=2
    unlock_shared(pi);
    print_use_count(s,pi);//pi use_count()=1
}

然后我们可以实现原来的例子如下:

class MyClass:public enable_shared_from_this { /*...*/ };

/* ... */
auto pobj = make_shared<MyClass>(...);
/* ... */
lock_shared(pobj);
system_api_send_obj(pobj.get());
/* ... */
auto preceived = 
    static_cast<MyClass *>(system_api_reveive_obj())->shared_from_this();
unlock_shared(preceived);

这个想法很容易实现一个enable_lockable_shared_from_this.然而,以上是更通用的,允许控制不是从enable_lockable_from_this`模板类派生的东西.

原文地址:https://www.jb51.cc/c/114000.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐