2014-08-28 9 views
5

Ich muss eine thread-sichere Karte machen, wobei ich meine, dass jeder Wert unabhängig Mutexed sein muss. Zum Beispiel muss ich in der Lage sein, map["abc"] und map["vf"] gleichzeitig aus 2 verschiedenen Threads zu bekommen.Karte von Mutex C++ 11

Meine Idee ist, zwei Karten zu erstellen: ein für Daten und einen für Mutex für jeden Schlüssel:

class cache 
{ 
private: 
.... 

    std::map<std::string, std::string> mainCache; 
    std::map<std::string, std::unique_ptr<std::mutex> > mutexCache; 
    std::mutex gMutex; 
..... 
public: 
    std::string get(std::string key); 

}; 
std::string cache::get(std::string key){ 
    std::mutex *m; 
    gMutex.lock(); 
    if (mutexCache.count(key) == 0){ 
     mutexCache.insert(new std::unique_ptr<std::mutex>); 
    } 
    m = mutexCache[key]; 
    gMutex.unlock(); 
} 

finde ich, dass ich nicht Karte von String Mutex erstellen kann, weil es keine Kopie Konstruktor in std::mutex und ich muss std::unique_ptr verwenden; aber wenn ich das kompiliere bekomme ich:

/home/user/test/cache.cpp:7: error: no matching function for call to 'std::map<std::basic_string<char>, std::unique_ptr<std::mutex> >::insert(std::unique_ptr<std::mutex>*)' 
     mutexCache.insert(new std::unique_ptr<std::mutex>); 
                 ^

Wie löse ich dieses Problem?

Antwort

9

ersetzen mutexCache.insert(new std::unique_ptr<std::mutex>) mit:

mutexCache.emplace(key, new std::mutex); 

In C++ 14, sollten Sie sagen:

mutexCache.emplace(key, std::make_unique<std::mutex>()); 

Der gesamte Code ist sehr laut und unelegant, though. Es sollte wohl so aussehen:

std::string cache::get(std::string key) 
{ 
    std::mutex * inner_mutex; 

    { 
     std::lock_guard<std::mutex> g_lk(gMutex); 

     auto it = mutexCache.find(key); 
     if (it == mutexCache.end()) 
     { 
      it = mutexCache.emplace(key, std::make_unique<std::mutex>()).first; 
     } 
     inner_mutex = it->second.get(); 
    } 

    { 
     std::lock_guard<std::mutex> c_lk(*inner_mutex); 
     return mainCache[key]; 
    } 
} 
+1

+1 für 'std :: make_unique ' –

+3

FWIW, sollten Sie 'make_unique' in C++ 11 verwenden. –

4

Warum benötigen Sie einen std::unique_ptr in erster Linie zu benutzen?

Ich hatte das gleiche Problem, wenn ich ein std::map von std::mutex Objekte erstellen musste. Das Problem ist, dass std::mutex weder kopierbar noch beweglich ist, also musste ich es "an Ort und Stelle" konstruieren.

Ich konnte nicht einfach emplace verwenden, weil es nicht direkt für default-konstruierte Werte funktioniert. Es gibt eine Option std::piecewise_construct, so zu verwenden:

map.emplace(std::piecewise_construct, std::make_tuple(key), std::make_tuple()); 

aber es ist IMO kompliziert und weniger lesbar.

Meine Lösung ist viel einfacher - verwenden Sie einfach operator[] - es wird den Wert mit seinem Standardkonstruktor erstellen und einen Verweis darauf zurückgeben. Oder es wird nur ein Verweis auf den bereits vorhandenen Artikel gefunden und zurückgegeben, ohne einen neuen Artikel zu erstellen.

std::map<std::string, std::mutex> map; 

std::mutex& GetMutexForFile(const std::string& filename) 
{ 
    return map[filename]; // constructs it inside the map if doesn't exist 
}