[英]Attemption to create thread safe std::map
假設我們有std::map
容器,並且希望使其在插入,擦除,搜索和編輯記錄方面安全。 同時,我們希望線程可以並行處理不同的記錄(讀取和編輯記錄)。 為此,我制作了一個單獨的記錄類-編輯操作,該操作受互斥鎖保護。
class Data
{
public:
Data(const std::string& data) : _mutex(), _data(data) { }
void setData(const std::string& data)
{
std::lock_guard<std::mutex> locker(_mutex);
_data = data;
}
const std::string& getData() const { return _data; }
private:
std::mutex _mutex;
std::string _data;
};
class Storage
{
public:
void insertData(size_t key, const std::string& data)
{
std::lock_guard<std::mutex> locker(_mutex);
_storage[key] = data;
}
void eraseData(size_t key)
{
std::lock_guard<std::mutex> locker(_mutex);
_storage.erase(key);
}
const std::string& getData(size_t key) const { return _storage[key].getData(); }
void setData(size_t key, const std::string& data) { _storage[key].setData(data); }
private:
std::mutex _mutex;
std::map<size_t, Data> _storage;
};
現在,假設線程抓取要編輯的某些記錄的“本地”互斥量( Data::setData
方法調用)。 同時,其他線程將“全局”互斥鎖刪除,以刪除該記錄(調用Storage::eraseData
方法)-有什么問題嗎? 此代碼中還有哪些其他問題?
首先解決您的並發問題。 這是一個C ++ 14解決方案,因為C ++ 11版本更為冗長,並且我們沒有想要的所有鎖定原語:
template<class T>
struct thread_safe {
template<class F>
auto read( F&& f ) const {
std::shared_lock<decltype(mutex)> lock(mutex);
return std::forward<F>(f)(t);
}
template<class F>
auto write( F&& f ) {
std::unique_lock<decltype(mutex)> lock(mutex);
return std::forward<F>(f)(t);
}
template<class O>
thread_safe(O&&o):t(std::forward<O>(o)) {}
thread_safe() = default;
operator T() const {
return o.read([](T const& t){return t;});
}
// it is really this simple:
thread_safe( thread_safe const& o ):t( o ) {}
// forward to above thread safe copy ctor:
thread_safe( thread_safe & o ):thread_safe( const_cast<thread_safe const&>(o) ) {}
thread_safe( thread_safe && o ):thread_safe(o) {}
thread_safe( thread_safe const&& o ):thread_safe(o) {}
thead_safe& operator=( thread_safe const& o ) {
write( [&o](auto& target) {
target = o;
});
return *this;
}
template<class O>
thread_safe& operator=( O&& o ) {
write([&o](auto& t){ t = std::forward<O>(o); });
return *this;
}
private:
T t;
mutable std::shared_timed_mutex mutex;
};
這是圍繞任意類的線程安全包裝器。
我們可以直接使用它:
typedef thread_safe< std::map< size_t, thread_safe<std::string> > > my_map;
這里我們有兩級線程安全映射。
使用示例,將條目33設置為"hello"
:
my_map.write( [&](auto&& m){
m[33] = "hello";
} );
在每個元素和整個地圖上都有許多讀者,一位作家。 從返回的迭代器read
或write
呼叫是不是安全。
當然,您應該測試和審核上面的代碼。 我沒有
核心思想很簡單。 要閱讀,你必須.read
線程安全的對象。 您傳入的Lambda會獲得對基礎數據的const&
。 在std::
數據上,保證這些數據是多讀取器安全的。
要編寫,您必須.write
。 這將獲得排他鎖,從而阻止其他.read
。 此處的lambda對基礎數據獲取&
。
我添加了operator T
和=
以及copy-construct,以使類型更規則。 這樣做的代價是您可能會意外地產生很多鎖定/解鎖行為。 優點是m[33] = "hello"
可以正常工作 ,真棒。
您有兩個大問題:
如果一個線程同時調用insertData
,而另一個線程調用getData
什么呢? 調用operator[]
可能會崩潰,因為嘗試訪問地圖時正在對其進行修改。
如果一個線程調用eraseData
而另一個線程仍在使用從getData
返回的引用,會發生什么情況? 該引用可能無效,從而導致崩潰。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.