patterncppMinor
Thread-safe std::map accessor
Viewed 0 times
stdmapthreadsafeaccessor
Problem
After learning that
```
#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)
{
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
To compile, you will probably need to add some special flag to your compiler. For example, g++ would be:
The
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.ccThe
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.ccContext
StackExchange Code Review Q#8715, answer score: 3
Revisions (0)
No revisions yet.