HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Thread-safe std::map accessor

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
stdmapthreadsafeaccessor

Problem

After learning that std::map containers are not inherently atomic and therefore not thread-safe (check out this related Stack Overflow question and usage example), I decided to create code that would allow concurrent access to the container.

```
#ifndef MAP_GUARD_H_
#define MAP_GUARD_H_

/*
This class was designed to make the standard map synchronized for the basic functions.
The intent was for it to be filled with shared_ptr as the value in the pair.
A Grand Central Dispatch serial queue is used for syncronising access to the map rather than raw mutexes.
*/

// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)

#include
#ifdef WIN32
#include
#else
#include
#endif

template
class map_guard
{
public:
map_guard();
~map_guard();
void clear();
void erase(const K& key);
void insert(const K& key, const V& value);
/ Take a snopshot of map_ and fill target with it (usefull if you want to use iterators) /
void fill_map(std::map& target) const;
V find(const K& key) const;
size_t size() const;
bool empty() const;
void swap(std::map& map);
private:
void make_swap(std::map* map);
void get(std::map* target) const;
void get(const K& key, V* target) const;
void get_empty(bool* empty) const;
void get_size(size_t* size) const;
std::map map_;
xdispatch::queue* dispatch_queue_;
DISALLOW_COPY_AND_ASSIGN(map_guard);
};

template
map_guard::map_guard() : dispatch_queue_(0)
{
dispatch_queue_ = new xdispatch::queue("test");
}

template
map_guard::~map_guard()
{
if(dispatch_queue_)
{
delete dispatch_queue_;
dispatch_queue_ = 0;
}
}

template
void map_guard::clear()
{
dispatch_queue_->sync(${
map_.clear();
});
}

template
void map_guard::erase(const K& key)
{

Solution

If you would like to try out c++0x, you have std::mutex and std::lock_guard. An example forwarding some of methods in std::map is shown as following:

//hello.cc

#include 
#include 
#include 

template , class Allocator = std::allocator > >
class guarded_map {
    private:
        std::map _map;
        std::mutex _m;

    public:
        void set(K key, V value) {
            std::lock_guard lk(this->_m);
            this->_map[key] = value;
        }

        V & get(K key) {
            std::lock_guard lk(this->_m);
            return this->_map[key];
        }

        bool empty() {
            std::lock_guard lk(this->_m);
            return this->_map.empty();
        }

        // other public methods you need to implement
};

int main(int argc, char ** argv) {
    guarded_map m;
    m.set(1, 10);
    m.set(2, 20);
    m.set(4, 30);
    std::cout<<"m[2]="<<m.get(2)<<std::endl;
    return 0;
}


To compile, you will probably need to add some special flag to your compiler. For example, g++ would be:

g++ -std=c++0x -o hello hello.cc


The std::lock_guard here makes things much easier. It locks the mutex in its constructor, and automatically unlock the mutex in its destructor, which means you can initialize a std::lock_guard at the beginning of a block you need to synchronize concurrency, and leave it there, as the lock will be released when this block exit.

Code Snippets

//hello.cc

#include <map>
#include <mutex>
#include <iostream>

template <class K, class V, class Compare = std::less<K>, class Allocator = std::allocator<std::pair<const K, V> > >
class guarded_map {
    private:
        std::map<K, V, Compare, Allocator> _map;
        std::mutex _m;

    public:
        void set(K key, V value) {
            std::lock_guard<std::mutex> lk(this->_m);
            this->_map[key] = value;
        }

        V & get(K key) {
            std::lock_guard<std::mutex> lk(this->_m);
            return this->_map[key];
        }

        bool empty() {
            std::lock_guard<std::mutex> lk(this->_m);
            return this->_map.empty();
        }

        // other public methods you need to implement
};

int main(int argc, char ** argv) {
    guarded_map<int, int> m;
    m.set(1, 10);
    m.set(2, 20);
    m.set(4, 30);
    std::cout<<"m[2]="<<m.get(2)<<std::endl;
    return 0;
}
g++ -std=c++0x -o hello hello.cc

Context

StackExchange Code Review Q#8715, answer score: 3

Revisions (0)

No revisions yet.