《C++并发编程实战》笔记-第6章设计基于锁的并发数据结构

如何只保留最必要的串行操作,将串行操作减少至最低程度,并且最大限度地实现真正的并发访问?

1)队列,如果wait_and_pop()产生异常(如新指针shared_ptr<>在构建时就有可能产生异常),就不会有新线程被唤醒。
解决:将shared_ptr<>的初始化移到push()调用处,令队列改为存储shared_ptr<>,从内部复制shared_ptr<>实例的操作不会抛出异常,所以wait_and_pop()异常安全。此外,shared_ptr<>实例内存分配可以脱离锁保护。

/*
线程间传递数据,队列,条件变量控制同步
条件变量的wait和unique_lock<>一起使用,而不和lock_guard<>一起使用,因为要灵活解锁
*/
template<typename T>
class threadsafe_queue
{
	queue<shared_ptr<T>> data;
	mutable mutex m;
	condition_variable cond;
public:
	threadsafe_queue(){}

	threadsafe_queue(const threadsafe_queue& other)
	{
		scoped_lock lock(m);
		data = other.data;
	}

	threadsafe_queue& operator=(const threadsafe_queue&) = delete;

	void push(T new_data)
	{
		shared_ptr<T> newdata(make_shared<T>(move(new_data)));
		scoped_lock lock(m);
		data.push(newdata);
		cond.notify_one();
	}

	bool try_pop(T& value)
	{
		scoped_lock lock(m);
		if (data.empty())
			return false;
		value = move(*data.front());
		data.pop();
		return true;
	}

	shared_ptr<T> try_pop()
	{
		scoped_lock lock(m);
		if (data.empty())
			return shared_ptr<T>();
		shared_ptr<T>  res = data.front();
		data.pop();
		return res;
	}

	void wait_and_pop(T& value)
	{
		unique_lock lock(m);
		cond.wait(lock, [this] {return !data.empty(); });
		value = move(*data.front());
		data.pop();
	}

	shared_ptr<T> wait_and_pop()
	{
		unique_lock lock(m);
		cond.wait(lock, [this] {return !data.empty(); });
		shared_ptr<T> res = data.front();
		data.pop();
		return res;
	}

	bool empty() const
	{
		scoped_lock lock(m);
		return data.empty();
	}
};

2)使用单链表,带有精细粒度锁的线程安全队列
1.预先设立一个不含数据的虚位节点,这样head->next和tail->next不会出现竞争,即没有可能指向同一节点。此外,push()可以只操作tail指针。但是,为了容纳虚位节点,需通过指针间接存储数据,额外增加一个访问级别。
2.get_tail()调用在互斥head_mutex的保护内,保证了head指针不可能越过get_tail()所返回的位置,不变量保持成立。
3.try_pop()只在互斥tail_mutex短暂持锁,以保护tail指针读取,所以try_pop()整个调用过程几乎都可以与push()并发执行。队列节点通过unique_ptr<>的析构函数删除,操作开销大,在互斥外执行。
4.push()的新节点和新数据的内存分配脱离锁保护。

/*
使用单链表,带有精细粒度锁的线程安全队列,支持等待功能
wait_for_data()等待数据加入空队列,以将其弹出。返回锁,保证了头结点弹出的全过程都持有同一个锁
*/
template<typename T>
class threadsafel_queue
{
private:
	struct node
	{
		shared_ptr<T> data;
		unique_ptr<node> next;
	};

	unique_ptr<node>head;
	node* tail;
	mutex head_m;
	mutex tail_m;
	condition_variable cond;

	node* get_tail()
	{
		scoped_lock tail_lock(tail_m);
		return tail;
	}

	unique_ptr<node> pop_head()
	{
		unique_ptr<node>old_head = move(head);
		head = move(old_head->next);
		return old_head;
	}

	unique_lock<mutex> wait_for_data()
	{
		unique_lock head_lock(head_m);
		cond.wait(head_lock, [this] {return head.get() != get_tail(); });
		return move(head_lock);
	}
	
	unique_ptr<node> wait_pop_head()
	{
		unique_lock head_lock(wait_for_data());
		return pop_head();
	}
	
	unique_ptr<node> wait_pop_head(T& value)
	{
		unique_lock head_lock(wait_for_data());
		value = move(*head->data);
		return pop_head();
	}

	unique_ptr<node> try_pop_head()
	{
		scoped_lock head_lock(head_m);

		if (head.get() == get_tail())
		{
			return unique_ptr<node>();
		}
		return pop_head();
	}

	unique_ptr<node> try_pop_head(T & value)
	{
		scoped_lock head_lock(head_m);

		if (head.get() == get_tail())
		{
			return unique_ptr<node>();
		}
		value = move(*head->data);
		return pop_head();
	}


public:
	threadsafel_queue() :head(new node), tail(head.get()) {}
	~threadsafel_queue()
	{
		delete tail;
	}
	threadsafel_queue(const threadsafel_queue& other) = delete;
	threadsafel_queue& operator=(const threadsafel_queue& other) = delete;

	shared_ptr<T>wait_and_pop()
	{
		return wait_pop_head()->data;
	}

	void wait_and_pop(T & value)
	{
		wait_pop_head(value);
	}

	shared_ptr<T> try_pop()
	{
		unique_ptr<node>old_head = try_pop_head();
		return old_head ? old_head->data : shared_ptr<T>();
	}

	bool try_pop(T& value)
	{
		return try_pop_head(value);
	}

	void push(T new_value)
	{
		shared_ptr<T> new_data(make_shared<T>(move(new_value)));
		unique_ptr<node>p(new node);
		node* new_tail = p.get();
		{
			scoped_lock tail_lock(tail_m);
			tail->data = new_data;
			tail->next = move(p);
			tail = new_tail;
		}
		cond.notify_one();
	}

	bool empty()
	{
		scoped_lock head_lock(head_m);
		return (head.get() == get_tail());
	}
	

};

3)使用散列表,带有精细粒度锁的线程安全查找表
1.每个键属于一个桶,可以为每个桶使用独立的锁。
2.再使用共享锁,支持多个读线程或一个写线程。
3.数据快照,以快照形式获取查找表的当前状态,并保存为map<>,该功能所有桶被锁住。
:find_entry_for()函数后加了const,那data.begin()和data.end()返回const_iterator,进而find_if返回const_iterator,无法转换为bucket_iterator。即不能把const迭代器转为非const迭代器
解决:find_entry_for重载,一个加const,用于value_for(),一个不加const用于add_or_update_mapping()和remove_mapping()。
注意:const成员函数,要用mutable。scoped_lock删去了拷贝构造,要用unique_lock。

/*
使用散列表,带有精细粒度锁的线程安全查找表
*/
template<typename Key,typename Value,typename Hash=hash<Key>>
class threadsafe_lookup_table
{
private:
	class bucket_type
	{
	public:
		typedef pair<Key, Value> bucket_value;
		typedef list<bucket_value> bucket_data;
		using  bucket_iterator = typename bucket_data::iterator;
		using  bucket_const_iterator = typename bucket_data::const_iterator;
		bucket_data data;
		mutable shared_mutex m;

		bucket_const_iterator find_entry_for(const Key& key) const
		{
			return find_if(data.begin(), data.end(), [&](const bucket_value& item) {return item.first == key; });
		}

		bucket_iterator find_entry_for(const Key& key) 
		{
			return find_if(data.begin(), data.end(), [&](const bucket_value& item) {return item.first == key; });
		}
	public:
		Value value_for(const Key& key, const Value& dafault_value) const
		{
			shared_lock lock(m);
			bucket_const_iterator found_entry=find_entry_for(key);
			return (found_entry == data.end()) ? dafault_value : found_entry->second;
		}

		void add_or_update_mapping(const Key& key, const Value& value)
		{
			scoped_lock lock(m);
			bucket_iterator found_entry = find_entry_for(key);
			if (found_entry == data.end())
				data.push_back(bucket_value(key, value));
			else
				found_entry->second = value;
		}

		void remove_mapping(const Key& key)
		{
			scoped_lock lock(m);
			bucket_iterator found_entry = find_entry_for(key);
			if (found_entry != data.end())
				data.erase(found_entry);
		}
	};

	vector<unique_ptr<bucket_type>> buckets;
	Hash hasher;
	bucket_type& get_bucket(const Key& key) const
	{
		const size_t bucket_index = hasher(key) % buckets.size();
		return *buckets[bucket_index];
	}

public:
	threadsafe_lookup_table(unsigned  num_bucket = 19) :buckets(num_bucket),hasher(Hash())
	{
		for (unsigned i=0;i<num_bucket;++i)
		{
			buckets[i].reset(new bucket_type);
		}
	}
	threadsafe_lookup_table(const threadsafe_lookup_table& other) = delete;
	threadsafe_lookup_table& operator=(const threadsafe_lookup_table& other) = delete;

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

	void add_or_update_mapping(const Key& key, const Value& value)
	{
		get_bucket(key).add_or_update_mapping(key, value);
	}

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

	map<Key, Value> get_map() const
	{
		vector<unique_lock<shared_mutex>>locks;

		for (unsigned i=0;i<buckets.size();++i)
		{
			locks.push_back(unique_lock(buckets[i]->m));
		}

		map<Key, Value>res;

		for (unsigned i = 0; i < buckets.size(); ++i)
		{
			for (auto it:buckets[i]->data)
				res.insert(it);
		}

		return res;
	}
};

4)带有精细粒度锁的线程安全链表,支持迭代功能
1.每个节点都具备自己的互斥,可以在链表不同部分执行真正的并发操作:每个操作仅需锁住目标节点,当操作转移到下一个目标节点,原来的锁即可解开。
2.使用unique_ptr<>,出了作用域,指针自动销毁,同时连带删除目标节点。
3.remove_if中,销毁行为在解锁后发生,否则销毁已锁互斥是未定义行为

/*
带有精细粒度锁的线程安全链表,支持迭代功能
*/
template<typename T>
class threadsafe_list
{
private:
	struct node
	{
		mutex m;
		shared_ptr<T> data;
		unique_ptr<node>next;
		node():next(){}
		node(const T& value):data(make_shared<T>(move(value))){}
	};

	unique_ptr<node> head;
public:
	threadsafe_list():head(new node){}
	threadsafe_list(const threadsafe_list& other) = delete;
	threadsafe_list& operator=(const threadsafe_list& other) = delete;
	
	void push_front(const T& value)
	{
		unique_ptr<node>new_node(new node(value));
		scoped_lock lock(head->m);
		new_node->next = move(head->next);
		head->next = move(new_node);
	}

	template<typename Function>
	void for_each(Function f)
	{
		auto current = head.get();
		unique_lock lock(head->m);
		while (auto next = current->next.get())
		{
			unique_lock next_lock(next->m);
			lock.unlock();
			f(*next->data);
			current = next;
			lock = move(next_lock);
		}
	}

	template<typename Predicate>
	shared_ptr<T> find_first_if(Predicate p)
	{
		auto current = head.get();
		unique_lock lock(head->m);
		while (auto next = current->next.get())
		{
			unique_lock next_lock(next->m);
			lock.unlock();
			if (p(*next->data))
			{
				return next->data;
			}
			current = next;
			lock = move(next_lock);
		}
		return shared_ptr<T>();
	}

	template<typename Predicate>
	void remove_if(Predicate p)
	{
		auto current = head.get();
		unique_lock lock(head->m);
		while (auto next = current->next.get())
		{
			unique_lock next_lock(next->m);
			if (p(*next->data))
			{
				unique_ptr<node> remove_node = move(current->next);
				current->next = move(remove_node->next);
				next_lock.unlock();
			}
			else
			{
				lock.unlock();
				current = next;
				lock = move(next_lock);
			}
		}
	}

};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值