第七章 lock_free 解析 count reference queue

较之之前的使用引用计数来实现stack不同,因为queue涉及的两个操作分别对应于head tail指针

因此如果简单的使用前面的技术来设计lock_free算法就不再适合,因为有可能会出现这种情况:

tail指针离开其指向的节点之后,其他线程释放了这个节点,但是head指针还没有访问这个节点,

于是乎,之后如果有人调用pop。便会undefined behavior
所以应该设置一个__m_external_count
这在__node节点中 它被初始化为2,只有当tail head 访问时才减去它,分别对应之后的函数__free_external_count

具体要理解话,请阅读本章stack的实现,你便会了解我的意思(以上均为本人心得,同是初学者,可能理解有偏差,具体读者可以参考这本书的内容,

有疑问也可以留言  邮箱 [email protected]



#include <iostream>
#include <atomic>
#include <memory>
#include <thread>
#include <future>

#if defined _DEBUG
#include <crtdbg.h>
#define __MEMORY__LEAK__CHECK__ _CrtSetDbgFlag( \
	  _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | \
	 _CRTDBG_LEAK_CHECK_DF);
#define __leak__check__ __MEMORY__LEAK__CHECK__
#endif

namespace stc{
	template <typename _Ty>
	struct __node;

	template <typename _Ty>
	struct __count_node{
		typedef typename __node<_Ty>* __node_pointer;
		int	__m_external_count;
		__node_pointer __m_src;
		/**data member*/

		/**the consructor*/
		__count_node() :__m_external_count(0),
			__m_src(NULL){}
	};

	/**
	*	较之之前的使用引用计数来实现stack不同,因为queue涉及的两个操作分别对应于head tail指针
	*	因此如果简单的使用前几章的技术,有可能会出现这种情况:
	*	tail指针离开其指向的节点之后,其他线程释放了这个节点,但是head指针还没有访问这个节点,
	*	于是乎,之后如果有人调用pop。便会undefined behavior
	*	所以应该设置一个__m_external_count
	*	这在__node节点中 它被初始化为2,只有当tail head 访问时才减去它,分别对应之后的函数__free_external_count
	*	具体要理解话,请阅读本章stack的实现,你便会了解我的意思
	*/

	struct __inside_counter{
		int __m_internal_count : 30;
		unsigned int __m_external_count : 2;
	};/**the inside counter */

	template <typename _Ty>
	struct __node{
		typedef _Ty* __pointer;
		typedef _Ty& __reference;
		typedef typename std::atomic<__pointer> __atomic_data_type;
		typedef typename std::atomic<__count_node<_Ty>> __atomic_link_type;
		typedef typename std::atomic<__inside_counter> __atomic_count_type;
		__atomic_data_type  __m_data;
		__atomic_count_type __m_counter;
		__atomic_link_type  __m_next;
		__node() :__m_data(NULL){
			__inside_counter __counter_init;
			__counter_init.__m_external_count = 2;
			__counter_init.__m_internal_count = 0;
			__m_counter.store(__counter_init, std::memory_order_release);
		}
		void __release_reference();
	};

	template <typename _Ty>
	void __node<_Ty>::__release_reference(){
		__inside_counter __o_count = __m_counter.load(std::memory_order_relaxed);
		__inside_counter __n_count;
		do{
			__n_count = __o_count;
			--__n_count.__m_internal_count;
		} while (!__m_counter.compare_exchange_strong(__o_count, __n_count,
			std::memory_order_acquire, std::memory_order_relaxed));
		if (!__n_count.__m_external_count &&
			!__n_count.__m_internal_count){
			delete this;
		}
	}

	template <typename _Ty>
	class lk_free_que_s{
		typedef typename __count_node<_Ty> __count_node_type;
		typedef typename std::atomic<__count_node_type> __atomic_count_node_type;
	public:
		lk_free_que_s();
		~lk_free_que_s();
		std::unique_ptr<_Ty> pop();
		void push(const _Ty&);
	private:
		static void __increase_external_count(__atomic_count_node_type&, __count_node_type&);
		static void __free_external_count(__count_node_type&);
		void __set_tail(__count_node_type&, __count_node_type);
		void __init();
		void __destroy();
		void __clear();
		void __reset();
		__count_node_type __allocate_count_node();
	private:
		__atomic_count_node_type __m_head;
		__atomic_count_node_type __m_tail;
	};

	template <typename _Ty>
	lk_free_que_s<_Ty>::lk_free_que_s(){
		this->__init();
	}

	template <typename _Ty>
	lk_free_que_s<_Ty>::~lk_free_que_s(){
		this->__destroy();
	}

	template <typename _Ty>
	void lk_free_que_s<_Ty>::__init(){
		__count_node_type nil = __allocate_count_node();
		try{	
			__m_head.store(nil, std::memory_order_release);
			__m_tail.store(nil, std::memory_order_release);
		}catch (...){ 
			delete nil.__m_src;
			throw;
		}
	}

	template <typename _Ty>
	void lk_free_que_s<_Ty>::__destroy(){
		try{
			while (pop());
			delete __m_tail.load().__m_src;
		} catch (...){}
	}

	template <typename _Ty>
	typename lk_free_que_s<_Ty>::__count_node_type 
		lk_free_que_s<_Ty>::__allocate_count_node(){
			try{
				std::unique_ptr<__node<_Ty>> __src(new __node<_Ty>);
				__count_node_type __res;
				__res.__m_external_count = 1;
				__res.__m_src = __src.get();
				__src.release();
				return __res;
			}
			catch (...){ throw; }
	}

	/**
	*	此处两个函数的作用之前已经给出
	*/

	template <typename _Ty>
	void lk_free_que_s<_Ty>::__increase_external_count(
		__atomic_count_node_type& __head, __count_node_type& __o_counter){
		__count_node_type __n_counter;
		do{
			__n_counter = __o_counter;
			++__n_counter.__m_external_count;
		} while (!__head.compare_exchange_strong(__o_counter,__n_counter,
			std::memory_order_acquire,std::memory_order_relaxed));
		__o_counter.__m_external_count = __n_counter.__m_external_count;
	}

	template <typename _Ty>
	void lk_free_que_s<_Ty>::__free_external_count(__count_node_type& __o_counter){
		__node<_Ty>* __src = __o_counter.__m_src;
		assert(__src != NULL);
		const int __count_result = __o_counter.__m_external_count - 2;
		__inside_counter __o_inside_counter = __src->__m_counter.load(std::memory_order_relaxed);
		__inside_counter __n_inside_counter;
		do{
			__n_inside_counter = __o_inside_counter;
			--__n_inside_counter.__m_external_count;
			__n_inside_counter.__m_internal_count += __count_result;
		} while (!__src->__m_counter.compare_exchange_strong(__o_inside_counter,__n_inside_counter,
			std::memory_order_acquire,std::memory_order_relaxed));
		if (!__n_inside_counter.__m_external_count &&
			!__n_inside_counter.__m_internal_count){
			delete __src;
		}
	}

	template <typename _Ty>
	std::unique_ptr<_Ty> lk_free_que_s<_Ty>::pop(){
		__count_node_type __o_head = __m_head.load(std::memory_order_relaxed);//获得头部指针
		while (true){//increase count reference
			__increase_external_count(__m_head, __o_head);
			__node<_Ty>* __src = __o_head.__m_src;
			if (__src == __m_tail.load().__m_src){//如果__m_head __m_tail都是同一个元素 自然队列中是没有元素的
				__src->__release_reference();//decrease count reference
				return std::unique_ptr<_Ty>();//return null pointer
			}
			__count_node_type __next = __src->__m_next.load();//get the next node
			if (__m_head.compare_exchange_strong(__o_head, __next)){//if the head is't changed than the value of head will be the next node
				std::unique_ptr<_Ty> __res(__src->__m_data.exchange(NULL));
				__free_external_count(__o_head);//decrease count reference
				return __res;//return the result
			}
			__src->__release_reference();//decrease count reference
		}
	}

	template <typename _Ty>
	void lk_free_que_s<_Ty>::push(const _Ty& __data){
		std::unique_ptr<_Ty> __src(new _Ty(__data));
		_Ty* __o_data = NULL;
		__count_node_type __n_next = __allocate_count_node();//分配一个新的节点
		__count_node_type __o_tail = __m_tail.load();//获得尾部指针
		while (true){
			__increase_external_count(__m_tail, __o_tail);
			__o_data = NULL;//如果成功更新了数据,那么我们下一步就是跟新尾部指针 __m_tail 使之指向新分配的节点
			if (__o_tail.__m_src->__m_data.compare_exchange_strong(__o_data, __src.get())){
				__count_node_type __o_next;//如果没有线程帮助这个线程设置好尾部指针
				//那么现在的next域应该还是默认构造函数构造出来的__count_node_type 的object
				if (!__o_tail.__m_src->__m_next.compare_exchange_strong(__o_next, __n_next)){
					delete __n_next.__m_src;//如果已经有线程更新了__m_tail,自然刚才从堆中分配的__node就不需要
					__n_next = __o_next;//指向之后一个元素
				}
				__set_tail(__o_tail, __n_next);//如果没有线程帮助跟新尾部指针,那么就要调用__free_external_count
				__src.release();//否则是要调用__release_reference的
				return;//释放智能指针,不然它会释放堆中的内存
			}else{//如果没有能跟新数据,那么就帮助其他线程跟新尾部指针 我们的宗旨就是让所有的线程都在忙碌之中
				__count_node_type __o_next;
				if (__o_tail.__m_src->__m_next.compare_exchange_strong(__o_next, __n_next)){
					__o_next = __n_next;//如果更新成功了
					__n_next.__m_src = new __node<_Ty>;//自然__n_next还要帮助当前这个线程把它要加入的__data push
				}//到队列之中,所以还要重新分配__m_src
				__set_tail(__o_tail, __o_next);
			}
		}
	}

	template <typename _Ty>
	void lk_free_que_s<_Ty>::__set_tail(__count_node_type& __o_tail, __count_node_type __next){
		__node<_Ty>* __src = __o_tail.__m_src;//保存之前的__src
		__m_tail.compare_exchange_strong(__o_tail, __next);//如果对比成功了,自然是调用__free_external_count
		if (__o_tail.__m_src == __src)//否则只能调用__release_reference
			__free_external_count(__o_tail);
		else __src->__release_reference();
	}
}


你可能感兴趣的:(第七章 lock_free 解析 count reference queue)