如何正确移动/从shared_ptr读取
我的程序中有一个缓存系统。我有一个单独的静态类来维护这个缓存,并且同时在多个线程中使用缓存。我遇到了正确维护缓存系统的问题。这里是一些示例代码。如何正确移动/从shared_ptr读取
class db_cache
{
public:
typdef std::map<int, int> map_t;
static void update_cache();
static boost::shared_ptr< const map_t > get_cache();
private:
db_cache();
static void run_update();
static boost::shared_ptr< const map_t > cur_cache_;
static boost::shared_ptr< const map_t > old_cache_;
};
void db_cache::update_cache()
{
cur_cache_ = boost::make_shared<map_t>();
old_cache_ = boost::make_sahred<map_t>();
//
//Setup connection to server that sends updates
//
//Initialize cache
run_update();
while(true)
{
if(recv().compare("update") == 0)
{
//Update cache if update message recieved
run_update();
}
}
}
void db_cache::run_update()
{
//Create new cache to swap with current cache
auto new_cache = boost:make_shared<map_t>();
//
//Put data in new cache
//
boost::atomic_store(&old_cache_, boost::move(cur_cache_));
boost::atomic_store(&cur_cache_, boost::shared_ptr< const map_t >(boost::move(new_cache)));
}
auto db_cache::get_cache() -> boost::shared_ptr< const map_t >
{
return boost::atomic_load(&cur_cache_);
}
我目前在boost::atomic_store(&old_cache_, boost::move(cur_cache_));
发生崩溃。崩溃似乎是因为old_cache_
为空。这似乎在第二次收到更新消息时发生。我假设发生了什么(不是100%肯定,但我能想到的只有一条路),是:
- 第一时间收到消息,
cur_cache_
被复制到old_cache_
。 -
cur_cache_
被替换为new_cache
,导致旧的cur_cache_
(old_cache_
当前也指向的)为空。 -
old_cache_
因boost::atomic_store
由于为空而再次被调用时会导致崩溃。
我的问题是,为什么boost::atomic_store(&old_cache_, boost::move(cur_cache_));
不会导致参考计数器cur_cache_
增加。我能做到这一点吗?
其他说明:
我之所以old_cache_
是因为我相信从缓存中,这是最有可能也是一个问题读书时,我有一个问题。当我尝试从get_cache()
返回的地图中读取元素时,我的程序似乎崩溃了,所以为了确保它们保持在范围内,直到所有当前有副本的线程都完成为止,我保存最后一个版本的缓存。我想如果我有正确的方法来做到这一点,我可以摆脱old_cache_
一起,这应该解决上述问题。
编辑:
下面是使用缓存的代码:
//Vector big, don't want to copy
const std::vector *selected_vec = &(*db_cache::get_cache()).at(get_string);
for(std::vector<std::string>::iterator it = selected_vec->begin(), e = selected_vec->end(); it != e; ++it)
{
//String can be big, don't want to copy
const std::string *cur_string = &(*it);
if(cur_string == nullptr || cur_string->size() == 0)
{
continue;
}
char a = cur_string->at(0); //Crash here
//Do Stuff
}
我的实际map_t
类型是std::map<std::string,std::vector<std::string>>
类型不是std::map<int,int>
。在调用get_cache()
后,我得到我想要的矢量,并在矢量上进行迭代。对于每个字符串,我尝试获取第一个字符。当我尝试获取字符时,程序崩溃。我唯一能想到的就是selected_vec
已被删除。
您在run_update
有一场数据竞赛。其设定old_cache_
行:
boost::atomic_store(&old_cache_, boost::move(cur_cache_));
正在执行非原子修改cur_cache_
。回想的atomic_store
签名:
namespace boost {
template<class T>
void atomic_store(shared_ptr<T>* p, shared_ptr<T> r);
}
顺带表达boost::move(cur_cache_)
到第二个参数,你的函数由cur_cache_
移动和离开它设置为nullptr
创建实际参数的对象。即使此修改为原子,此行与设置cur_cache_
的较后行之间存在一个窗口,其中客户将看到从get_cache
返回的nullptr
。如果你绝对要保持在old_cache_
的cur_cache_
的价值,你需要使用atomic_exchange
同时设置cur_cache_
和检索旧值:
void db_cache::run_update()
{
auto new_cache = boost:make_shared<map_t>();
// ...
auto old = boost::atomic_exchange(&cur_cache_, boost::move(new_cache));
boost::atomic_store(&old_cache_, boost::move(old));
}
,但它会出现,你有没有用old_cache_
一次比赛是固定的,你可以完全消除:
void db_cache::run_update()
{
auto new_cache = boost:make_shared<map_t>();
// ...
boost::atomic_store(
&cur_cache_,
boost::shared_ptr<const map_t>(boost::move(new_cache))
);
}
你原来问题的根源是在这个“客户”代码:
const std::vector *selected_vec = &(*db_cache::get_cache()).at(get_string);
将指针存储到通过shared_ptr
访问的对象的内部,但不保留对该对象shared_ptr
所表示的对象的引用。当您稍后取消引用循环中的指针时,其指示对象可能已被销毁。你需要保持shared_ptr
周围的副本,以确保当你使用它(和你不妨使用引用代替指针)指涉保持活着:
boost::shared_ptr<const map_t> cache = db_cache::get_cache();
//Vector big, don't want to copy
const std::vector &selected_vec = cache->at(get_string);
for(std::vector<std::string>::iterator it = selected_vec->begin(), e = selected_vec->end(); it != e; ++it)
{
//String can be big, don't want to copy
const std::string &cur_string = *it;
if(cur_string.size() == 0)
{
continue;
}
char a = cur_string.at(0); //Don't crash here
//Do Stuff
}
我想我可以为我的崩溃问题提供一个答案,但我仍然认为有更好的方法来设计解决方案。基本上,我的三个步骤列表是有人正确的,boost::atomic_store(&old_cache_, boost::move(cur_cache_));
导致cur_cache_
有0个引用,并且对象cur_cache_
指向也被释放。下一次通过该函数,old_cache_然后指向释放的指针,并导致崩溃。我的解决办法是改变
boost::atomic_store(&old_cache_, boost::move(cur_cache_));
到
boost::atomic_store(&old_cache_, cur_cache_);
其停止cur_cache_
的use_count增加至两个,第一个电话后,第二个电话去后回落到1。
我仍然认为有一个更好的方式来构建我的代码,而不必保留old_cache_
,并会很乐意接受某人可以解释的答案。
大,谢谢你的'atomic_exchange'功能。至于第二部分,那原本就是我的,但我遇到了一个问题。我将用我的程序正在做的一些示例代码更新原始帖子。我想我的问题是,是否有任何理由可以想到,在使用'cur_cache'的线程完成运行之前,缓存将被删除。每次调用get_cache()时,我的'get_cache()'函数是否应该不增加'shared_ptr'的use_count? – Eumcoz 2015-02-24 14:14:29
@Eumcoz'get_cache'返回'shared_ptr'的一个副本,它*增加了'use_count'。客户端代码中的问题是,它会删除'shared_ptr'的副本 - 在访问'shared_ptr'指向的对象的内部之前,减少'use_count' - * *。 – Casey 2015-02-24 16:54:47
真棒,谢谢你的帮助,很有道理! – Eumcoz 2015-02-24 21:52:06