我正在尝试编写一个线程安全的映射,但从不锁定或阻塞读取。我的尝试是使用在写入时复制出来的只读映射。想法是 get()是无锁的, put()将当前的只读底层映射复制到新映射,执行put,并交换掉新的当前底层地图。 (是的, put()是低效的,因为它复制整个地图,但我不关心我的用例)
我第一次尝试使用std :: atomic< * StringMap>对于只读地图 BUT ,这有一个很大的错误,可能是由于我的java背景。 get()原子地获取一个指向底层地图的指针,当它加载它时可能是也可能不是当前的地图(这没关系)。但是 put()在将基础地图换成新地图之后删除它。如果 get()正在调用旧地图,就像它被删除一样,这显然会崩溃。
我的一位朋友建议 shared_ptr ,但他不确定shared_ptr操作是否在封面下进行任何锁定。 文档说这是线程安全的。 编辑:正如nosid指出的那样,它不是线程安全的,我需要std :: atomic的特殊原子操作。
所以我的问题是:1。这个算法是否可行? 2. shared_ptr操作是否进行任何锁定,尤其是在访问时?
#include <unordered_map>
#include <atomic>
#include <pthread.h>
#include <memory>
typedef std::unordered_map<std::string, std::string> StringMap;
class NonBlockingReadMap {
private:
pthread_mutex_t fMutex;
std::shared_ptr<StringMap> fspReadMapReference;
public:
NonBlockingReadMap() {
fspReadMapReference = std::make_shared<StringMap>();
}
~NonBlockingReadMap() {
//so, nothing here?
}
std::string get(std::string &key) {
//does this access trigger any locking?
return fspReadMapReference->at(key);
}
void put(std::string &key, std::string &value) {
pthread_mutex_lock(&fMutex);
std::shared_ptr<StringMap> spMapCopy = std::make_shared<StringMap>(*fspReadMapReference);
std::pair<std::string, std::string> kvPair(key, value);
spMapCopy->insert(kvPair);
fspReadMapReference.swap(spMapCopy);
pthread_mutex_unlock(&fMutex);
}
void clear() {
pthread_mutex_lock(&fMutex);
std::shared_ptr<StringMap> spMapCopy = std::make_shared<StringMap>(*fspReadMapReference);
fspReadMapReference.swap(spMapCopy);
spMapCopy->clear();
pthread_mutex_unlock(&fMutex);
}
};
答案 0 :(得分:2)
您的代码在std::shared_ptr
上包含数据竞争,并且在C ++中未定义具有数据竞争的程序的行为。
问题是:类std::shared_ptr
不是线程安全的。但是,std::shared_ptr
有一些特殊的原子操作,可用于解决问题。
您可以在以下网页上找到有关这些原子操作的更多信息:
答案 1 :(得分:1)
Reader应该做两个操作:get和put。 get总是检索新指针并增加原子计数。释放指针并减少。当计数变为零时删除地图。 作者创建了一个新的地图,然后开始了。然后它会对旧地图进行标记以将其标记为删除。