1. 程式人生 > >c++執行緒安全的map

c++執行緒安全的map

參考了 《c++併發程式設計實戰》這本書上的程式碼寫了一個執行緒安全可以併發的map

/*
 * threadsafe_map.cpp
 *
 *  Created on: May 11, 2018
 *      Author: clh01s
 *      執行緒安全的查詢表
 *      通過boost的shared_mutex實現讀寫鎖
 *      假設有一定數量的桶,每一個鍵都有一個桶
 *      這就意味著可以安全地為每一個桶分配一個獨立的鎖
 *      這樣一個桶就可以有1個併發N個桶就有N個併發
 */

#include <iostream>
#include <mutex>
#include <list>
#include <utility>//std::pair
#include <boost/thread/shared_mutex.hpp>//boost::shared_mutex
#include <vector>
#include <string>
#include <algorithm>    // std::find_if
#include <memory>
#include <functional>


template<typename Key,typename Value,typename Hash = std::hash<Key>>
class threadsafe_lookup_table
{
private:
	class bucket_type
	{
	public:
		//在這個函式讀取時將鎖鎖定
		Value value_for(Key const& key,Value const& default_value) const
		{
			boost::shared_lock<boost::shared_mutex> lock(_mutex);
			_bucket_const_iterator found_entry = find_entry_for(key);
//			std::cout<<"(found_entry == _data.end()) ? default_value : found_entry->second = "<<((found_entry == _data.end()) ? default_value : found_entry->second)<<std::endl;
			return (found_entry == _data.end()) ? default_value : found_entry->second;
		}

		//在修改時也將鎖鎖定
		void add_or_update_mapping(Key const& key,Value const& value)
		{
			std::unique_lock<boost::shared_mutex> lock(_mutex);
			_bucket_iterator found_entry = find_entry_for(key);
			if(found_entry == _data.end())
			{
				_data.push_back(_bucket_value(key,value));
			}
			else
			{
				//如果key已經存在就更新value
				found_entry->second = value;
			}
		}

		void remove_mapping(Key const& key)
		{
			std::unique_lock<boost::shared_mutex> lock(_mutex);
			_bucket_iterator const found_entry = find_entry_for(key);
			if(found_entry != _data.end())
			{
				_data.erase(found_entry);
			}
		}
	private:
		typedef std::pair<Key,Value> _bucket_value;
		typedef std::list<_bucket_value> _bucket_data;
		//const與非const版本的迭代器
		typedef typename _bucket_data::iterator _bucket_iterator;
		typedef typename _bucket_data::const_iterator _bucket_const_iterator;

		_bucket_data _data;
		//每一個桶都將被一個shared_mutex例項保護
		mutable boost::shared_mutex _mutex;

		//通過此函式確定是否有以key為關鍵字的桶可以用來存放資料
		//非const版本
		_bucket_iterator find_entry_for(Key const& key)
		{
			return std::find_if(_data.begin(),_data.end(),
			                [&](_bucket_value const& item)
			                {return item.first==key;});
		}

		//通過此函式確定是否有以key為關鍵字的桶可以用來存放資料
		//const版本
		_bucket_const_iterator find_entry_for(Key const& key) const
		{
			//由於要返回非const的迭代器所以將資料放置到非const變數返回
			return std::find_if(_data.begin(),_data.end(),
			                [&](_bucket_value const& item)
			                {return item.first==key;});
		}
	};

public:
	//持有桶的變數
	std::vector<std::unique_ptr<bucket_type> > buckets;
	Hash hasher;
	//獲取數量並不改變所以不需要鎖
	bucket_type& get_bucket(Key const& key) const
	{
		std::size_t const bucket_index = hasher(key) % buckets.size();
		std::cout<<"bucket_index = "<<bucket_index<<std::endl
				<<"hasher(key) = "<<hasher(key)<<std::endl
				<<"buckets.size() = "<<buckets.size()<<std::endl;
		return *buckets[bucket_index];
	}

public:
	typedef Key key_type;
	typedef Hash hash_type;
	//初始化數量,質數與雜湊效率最高所以選擇19初始化
	threadsafe_lookup_table(
			unsigned num_buckets = 19,Hash const& hasher_ = Hash()):
			buckets(num_buckets),hasher(hasher_)
	{
		for(unsigned i = 0;i < num_buckets;++i)
		{
			buckets[i].reset(new bucket_type);
		}
	}

	threadsafe_lookup_table(threadsafe_lookup_table const& other) = delete;

	threadsafe_lookup_table const& operator=(
			threadsafe_lookup_table const& other) = delete;

	Value value_for(Key const& key,Value const& default_value = Value())
	{
		return get_bucket(key).value_for(key,default_value);
	}

	void remove_maping(Key const& key)
	{
		get_bucket(key).remove_mapping(key);
	}
};

int main()
{
	threadsafe_lookup_table<std::string,std::string> my_map;
	//通過get_bucket的引數key來選擇你需要往哪個桶中存放資料
	//再呼叫add_or_update_mapping來存放你的key和value
	my_map.get_bucket("a").add_or_update_mapping("a","aa");
	//呼叫value_for來獲得key值
	//你會發現value_for也需要key和value這是應為value_for的value是預設值用來確定返回的是否是真的key值如果是預設值就代表沒有查詢到
	std::cout<<"key a for value ="<<my_map.get_bucket("a").value_for("a","0")<<std::endl;
	return 0;
}

執行結果:

[email protected]:~/testcode/併發$ g++ threadsafe_map.cpp -std=c++11  -L/usr/local/lib -I/usr/local/include/boost/ -lboost_system -lboost_thread -g
[email protected]:~/testcode/併發$ ./a.out
bucket_index = 16
hasher(key) = 4993892634952068459
buckets.size() = 19
bucket_index = 16
hasher(key) = 4993892634952068459
buckets.size() = 19
key a for value =aa

相關推薦

c++執行安全map

參考了 《c++併發程式設計實戰》這本書上的程式碼寫了一個執行緒安全可以併發的map/* * threadsafe_map.cpp * * Created on: May 11, 2018 * Author: clh01s * 執行緒安全的查詢

執行安全Map比較

如何執行緒安全的使用HashMap 2016年09月02日 13:37:32 標籤: java / hashmap / 執行緒安全 / 8116 編輯 刪除 在週二面試時,一面的面試官有問到HashMap

C++ 執行安全下Lock 類的兩種使用方式

“不定義,做一個保持好奇心的普通人”   꿈을 이루게 될 거예요. 2018.12.19 快三年了:   Mutex 又稱互斥量,C++ 11中與 Mutex 相關的類(包括鎖型別)和函式都宣告在 <mutex> 標頭檔案中,所以如果你需要使

C++執行安全容器的設計

    最近看到一本書,《C++併發程式設計實戰》,[美] Anthony Williams 著,裡面有談及執行緒安全容器的設計及實現程式碼,本人覺得這樣的設計有點問題,問題還是比較明顯的,寫在這裡,供讀者自己思考吧。      關於程式碼,可以在這裡下載: https:/

[C++][執行安全]單例模式下雙檢查鎖和執行

問題 在設計模式中,有一個很經典的模式-單例模式,它可能是實現上最簡單的模式,在程式碼中也經常使用,在單執行緒下,毫無疑問延遲化載入是比較常用的,但是在多執行緒條件下,單例模式的延遲載入可能就會出現一些問題。 如以下的程式碼: T* GetInstance(

C/C++ 執行安全佇列

一、簡介 執行緒安全是一個比較嚴肅的問題,如果處理不好,可能導致資料被破壞,程式崩潰等問題,如何來處理多執行緒的併發問題?在windows平臺有相應的api給你用於控制併發,如互斥鎖,訊號量,事件,臨界區等,定要熟練掌握,當然現在STL庫已強大到相容不同的硬體

C++ 執行安全的單例模式

廢話不多說,常用的程式碼積澱下來。 一、懶漢模式 即第一次呼叫該類例項的時候才產生一個新的該類例項,並在以後僅返回此例項。 需要用鎖,來保證其執行緒安全性:原因:多個執行緒可能進入判斷是否已經存在例項的if語句,從而non thread safety。 使用double-check來

【VS開發】C++執行安全

我們是多麼渴望各種C++類都是多執行緒安全的,然而一旦涉及到物件間的互動,這樣的渴望可能就只能是奢望了。下面,我們以設計一個雙向鏈結點為例,看看要使其多執行緒安全將會帶來一些什麼問題。 class DoublyLinedNode{        DoublyLinedNod

C/C++程式設計教訓----函式內靜態類物件初始化非執行安全C++11之前)

不少程式設計師在編寫程式的時候,會使用函式內靜態(static)變數,既能滿足函式內這個變數可以持久的記錄某些資訊,又使其訪問範圍的控制侷限於函式內。但函式內靜態類物件初始化是非執行緒安全的。 問題背景 在我們產品中對log4cxx做了一些簡單的封裝 (採用VS2005編譯),其中會

2017.10.20 C#跨執行操作控制元件的執行安全方法

C#跨執行緒操作控制元件的執行緒安全方法 在C#中,經常用到這樣一個場景,Windows Form程式啟動一個工作者執行緒執行一部分工作,這樣做是為了避免速度慢的工作如果直接呼叫會使得主Form停止響應一段時間。 既然啟動了執行緒,就避免不了執行緒之間資料傳遞的事情,相信你有很多種辦法

C++設計一個執行安全的懶漢單例模式

#incldue<iostream> #include<mutex> using namespace std; class CSingleton { public: static CSingleton* GetCSingleton() { if (_p ==

c/c++ 多執行 利用條件變數實現執行安全的佇列

多執行緒 利用條件變數實現執行緒安全的佇列 背景:標準STL庫的佇列queue是執行緒不安全的。 利用條件變數(Condition variable)簡單實現一個執行緒安全的佇列。 程式碼: #include <queue> #include <memory> #include

執行安全Map的實現方式3種

1. HashMap,TreeMap 未進行同步考慮,是執行緒不安全的。 2. HashTable 和 ConcurrentHashMap 都是執行緒安全的。區別在於他們對加鎖的範圍不同,HashTable 對整張Hash表進行加鎖,而ConcurrentHashMap將Hash表分為16桶(s

C errno是否是執行安全

本文同時發表在https://github.com/zhangyachen/zhangyachen.github.io/issues/138 在使用多執行緒時,遇到了一個問題:執行緒例程中如果需要使用errno全域性變數,如何保證errno的執行緒安全性?例如一個簡單的執行緒池程式碼: for(int i

C# 利用執行安全資料結構BlockingCollection實現多執行

using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using Danny.Infrastructure.Helper; names

C# 多執行之List的執行安全問題

網上關於List的執行緒安全問題將的很少,所以自己實驗了一把,發現確實是執行緒不安全的.所以當你在進行多執行緒程式設計中使用了共享的List集合,必須對其進行執行緒安全處理. List的Add方法是執行緒不安全的,List的原始碼中的Add方法,使用了每次噹噹前的元素達到上限,通過建立一個新的陣列例項,並給

高併發下map和chan實現的連結池的執行安全及效率

1.背景 上一次blog寫著寫著崩掉了,這次一定寫完一節儲存一節。 目前從事go語言的後臺開發,在叢集通訊時需要用到thrift的rpc。由於叢集間通訊非常頻繁且併發需求很高,所以只能採用連線池的形式。由於叢集規模是有限的,每個節點都需要儲存平行節點的連線,所以

C# 多執行呼叫靜態方法或者靜態例項中的同一個方法-方法內部的變數是執行安全

 C#  多執行緒呼叫靜態方法或者靜態例項中的同一個方法-方法內部的變數是執行緒安全的       using System;using System.Threading;using System.Threading.Tasks;using Sys

C++自定應執行安全資料結構(1)

執行緒安全的棧 該執行緒安全棧的作用是,允許多個執行緒對棧進行操作,不必再棧上進行加鎖,而是棧本身內部封裝了鎖的機制。操作的本身不是並行化的,因為不可能同時對棧既新增資料,又取出資料;其真正的意義是多個執行緒訪問時,避免上述不安全的情況發生。 #include <excep

golang執行安全map

網上找的協程安全的map都是用互斥鎖或者讀寫鎖實現的,這裡用單個協程來實現下,即所有的增刪查改操作都整合到一個goroutine中,這樣肯定不會出現多執行緒併發訪問的問題。 基本思路是後臺啟動一個長期執行的goroutine,阻塞的接受自己channel中的請求req,req分為不同的請求,