c ++ pthread - 如何使地图访问线程安全?

时间:2011-12-11 13:53:56

标签: c++ multithreading hashmap

我有一个地图作为成员变量和多个访问地图的线程(读写访问)。现在我必须确保只有一个线程可以访问地图。但是我该怎么做呢?对此有什么最好的解决方案?

4 个答案:

答案 0 :(得分:4)

Boost包含一些用于共享访问的很好的锁实现。看看documentation

在你的情况下,你可能需要一个读写锁,因为如果你有大量的读取和很少的写入,互斥锁可能是过度的。

答案 1 :(得分:3)

您需要同步对地图的访问权限,例如使用POSIX mutex。该链接有一些简单的例子,说明如何使用互斥变量。

答案 2 :(得分:1)

实际上,在给定时间只有一个线程应该访问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;
};

请注意迭代器,只有在当前线程保持互斥锁时才能安全地操作它们。

此外,我建议您严格控制地图,将其放入有意义的最小对象中,并仅提供您需要的操作。最少的方法可以访问地图,错过一个接入点的可能性就越小。

答案 3 :(得分:1)

如果您有最新的编译器,则可以使用std::mutex(基于boost实现)。这是C ++ 11的一部分,所以它并没有在任何地方实现。 gcc-4.6的效果相当不错。 底层实现是Linux中的POSIX线程和Windows中的Windows线程。