2

I have the following case (simplified):

/* 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 ) );
     }

 }; 

The context is important but let's focus on this part:

std::shared_ptr<Thing> find_or_create( ThingId id )
{
    if( auto thing = m_registry.find( id ) )
        return thing;
    return make_new_thing(); 
}

Here if simultaneous calls were made for this function, simultaneous calls to make_new_thing() could happen, which is valid if the Thing don't have the same id, but not if it doesn't. We can't remove ids from the Registry because of the concurrent_unordered_map implementation, so we have no way to check if the object is being created.

All this suggests that in this case, a synchronization mechanism is required. However, if I use something like a work queue, then I will have to provide a future, which currently is locking, but even with future.then() the caller could potentially wait a long time.

What I want is to avoid locking (with a mutex) if possible, and no future (in this particular case).

Do you see any way to do it without locking?

4

1 回答 1

0

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

您将牺牲内存并基本上创建自己的互斥锁和自旋等待。

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

于 2013-06-19T18:18:52.423 回答