我有以下情况(简化):
/* Register objects living
and retrieve them on demand if the object is still alive on request.
The interface have to be concurrency-safe.
*/
class Registry
{
public:
void add( const std::shared_ptr<Thing>& thing )
{ m_index.emplace_back( thing );
std::shared_ptr<Thing> find( ThingId id )
{
auto find_it = m_index.id( id );
if( find_it != end( m_index ) )
{
// we can't remove the index safely (see http://software.intel.com/sites/products/documentation/doclib/tbb_sa/help/index.htm )
return find_it->second.lock(); // null if the object don't exist anymore
}
return nullptr;
}
private:
tbb::concurrent_unordered_map< ThingId, std::weak_ptr<Thing> > m_index;
};
// Concurrency safe too.
class Workspace
{
Registry m_registry;
std::unique_ptr<Thing> make_new_thing( ThingId id ); // not important
public:
std::shared_ptr<Thing> find( ThingId id ) { return m_registry.find(id); }
/* The goal here is to either retrieve the existing object,
or to create it.
*/
std::shared_ptr<Thing> find_or_create( ThingId id )
{
// HERE IS THE PROBLEM!!!
if( auto thing = m_registry.find( id ) )
return thing;
return make_new_thing();
}
};
// Concurrency-safe too.
class Editor
{
Workspace& m_workspace;
tbb::concurrent_unordered_set<std::shared_ptr<Thing>> m_things;
public:
void add_target( ThingId id )
{
m_things.push( m_workspace.find_or_create( id ) );
}
};
背景很重要,但让我们关注这一部分:
std::shared_ptr<Thing> find_or_create( ThingId id )
{
if( auto thing = m_registry.find( id ) )
return thing;
return make_new_thing();
}
如果对此函数进行了同时调用,则可能会同时调用make_new_thing(),如果Thing没有相同的id,则有效,但如果不具有相同的id则无效。 由于concurrent_unordered_map实现,我们无法从注册表中删除id,因此我们无法检查是否正在创建对象。
所有这些都表明在这种情况下,需要同步机制。但是,如果我使用类似工作队列的东西,那么我将不得不提供一个当前正在锁定的未来,但即使使用future.then(),调用者也可能会等待很长时间。
我想要的是尽可能避免锁定(使用互斥锁),没有未来(在这种特殊情况下)。
你有没有办法在没有锁定的情况下做到这一点?
答案 0 :(得分:0)
您可以使用Things的数组或环形缓冲区以及原子操作。内置原子内在或处理器特定的CMPXCHG汇编操作码。
你会牺牲内存并基本上创建自己的互斥锁并旋转等待。
在最简单的实现中,您的ThingId将成为数组的索引。你的“查找或创建”将是一个比较+交换原子操作,如果数组中的点是空的,你将在已经创建的新对象中进行交换,但是如果该点不为空,则会删除预先创建的新对象或保存它对于下一个调用,但这需要更多的原子操作来实现对象存储的并发。