是否可以在没有锁定或提供未来的情况下找到或创建?

时间:2013-06-19 17:54:29

标签: c++ c++11 concurrency

我有以下情况(简化):

/* 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(),调用者也可能会等待很长时间。

我想要的是尽可能避免锁定(使用互斥锁),没有未来(在这种特殊情况下)。

你有没有办法在没有锁定的情况下做到这一点?

1 个答案:

答案 0 :(得分:0)

您可以使用Things的数组或环形缓冲区以及原子操作。内置原子内在或处理器特定的CMPXCHG汇编操作码。

你会牺牲内存并基本上创建自己的互斥锁并旋转等待。

在最简单的实现中,您的ThingId将成为数组的索引。你的“查找或创建”将是一个比较+交换原子操作,如果数组中的点是空的,你将在已经创建的新对象中进行交换,但是如果该点不为空,则会删除预先创建的新对象或保存它对于下一个调用,但这需要更多的原子操作来实现对象存储的并发。