2

次のケースがあります(簡略化):

/* 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 を持っていない場合には有効ですが、そうでない場合には有効ではありません。concurrent_unordered_map の実装のため、レジストリから ID を削除することはできません。そのため、オブジェクトが作成されているかどうかを確認する方法がありません。

これはすべて、この場合、同期メカニズムが必要であることを示唆しています。ただし、作業キューのようなものを使用する場合は、現在ロックされているフューチャーを提供する必要がありますが、future.then() を使用しても、呼び出し元は長時間待機する可能性があります。

私が望むのは、可能であれば(ミューテックスを使用して)ロックを回避し、将来(この特定のケースでは)を回避することです。

ロックせずにそれを行う方法はありますか?

4

1 に答える 1

0

モノの配列またはリング バッファー、およびアトミック操作を使用できます。組み込みのアトミックな組み込みまたはプロセッサ固有の CMPXCHG アセンブリ オペコード。

メモリを犠牲にして、本質的に独自のミューテックスとスピン待機を作成します。

最も単純な実装では、ThingId は配列へのインデックスになります。あなたの「検索または作成」は、比較+交換アトミック操作になります。配列内のスポットが空の場合は、既に作成された新しいオブジェクトを交換しますが、スポットが空でない場合は、事前に作成された新しいオブジェクトを削除するか、保存します次の呼び出しでは、オブジェクト ストアの同時実行のために、より多くのアトミック操作が必要になります。

于 2013-06-19T18:18:52.423 に答える