【发布时间】:2011-12-11 13:53:55
【问题描述】:
我有一个映射作为成员变量和多个访问映射的线程(读写访问)。现在我必须确保只有一个线程可以访问地图。但是我该怎么做呢?什么是最好的解决方案?
【问题讨论】:
-
哦,我不知道我可以/必须接受答案,但我现在做到了;)
标签: c++ multithreading hashmap
我有一个映射作为成员变量和多个访问映射的线程(读写访问)。现在我必须确保只有一个线程可以访问地图。但是我该怎么做呢?什么是最好的解决方案?
【问题讨论】:
标签: c++ multithreading hashmap
Boost 包含一些不错的共享访问锁实现。看看documentation。
在您的情况下,您可能需要一个读写锁,因为如果您有大量读取和很少写入,则互斥锁可能会过度使用。
【讨论】:
1_48_0(或其他)替换为 release。使用 release 将重定向到最新版本的 Boost。如果每个人都这样做,Google 将停止发布指向过时 Boost 文档的链接。
您需要同步对地图的访问,例如使用POSIX mutex。该链接有一些简单易懂的示例,说明如何使用互斥变量。
【讨论】:
实际上,在给定时间只有单个线程应该访问map 的前提有点不妥。
并发读取是可以的,您要避免的是在其他人正在读取地图时让线程修改地图。
根据您需要的粒度级别,您可能会考虑使用读取器/写入器锁,它可以让多个读取并行进行。
使用 Boost 演示了 here 的确切用法:
boost::shared_mutex _access;
void reader()
{
// get shared access
boost::shared_lock<boost::shared_mutex> lock(_access);
// now we have shared access
}
void writer()
{
// get upgradable access
boost::upgrade_lock<boost::shared_mutex> lock(_access);
// get exclusive access
boost::upgrade_to_unique_lock<boost::shared_mutex> uniqueLock(lock);
// now we have exclusive access
}
之后,只需方便地包装地图访问即可。例如,您可以使用通用代理结构:
template <typename Item, typename Mutex>
class ReaderProxy {
public:
ReaderProxy(Item& i, Mutex& m): lock(m), item(i) {}
Item* operator->() { return &item; }
private:
boost::shared_lock<Mutex> lock;
Item& item;
};
template <typename Item, typename Mutex>
class WriterProxy {
public:
WriterProxy(Item& i, Mutex& m): uplock(m), lock(uplock), item(i) {}
Item* operator->() { return &item; }
private:
boost::upgrade_lock<Mutex> uplock;
boost::upgrade_to_unique_lock<Mutex> lock;
Item& item;
};
您可以将它们用作:
class Foo {
typedef ReaderProxy< std::map<int, int>, boost::shared_mutex> Reader;
typedef WriterProxy< std::map<int, int>, boost::shared_mutex> Writer;
public:
int get(int k) const {
Reader r(map, m);
auto it = r->find(k);
if (it == r->end()) { return -1; }
return it->second;
}
void set(int k, int v) {
Writer w(map, m);
w->insert(std::make_pair(k, v));
}
private:
boost::shared_mutex m;
std::map<int, int> map;
};
但请注意迭代器,它们只能在互斥锁被当前线程持有时安全地操作。
另外,我建议您严格控制地图,将其放入有意义的最小对象中,并仅提供您需要的那些操作。访问地图的方法越少,您错过一个访问点的可能性就越小。
【讨论】:
如果你有一个最近的编译器,你可以使用std::mutex(它基于 boost 实现)。这是 C++11 的一部分,因此并未在任何地方实现。 gcc-4.6 工作得相当好。
底层实现是 linux 中的 POSIX 线程和 Windows 中的 Windows 线程。
【讨论】: