c++千万数据级别正确使用无锁队列,避免内存撕碎

Posted qianbo_insist

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++千万数据级别正确使用无锁队列,避免内存撕碎相关的知识,希望对你有一定的参考价值。

1、 内存

2、时间计算类

这个时间计算在我以前的文章里面都有

class TicToc

public:
	TicToc()
	
		tic();
	

	void tic()
	
		start = std::chrono::system_clock::now();
	

	double toc()
	
		end = std::chrono::system_clock::now();
		std::chrono::duration<double> elapsed_seconds = end - start;
		start = end;
		return elapsed_seconds.count() * 1000;
	

private:
	std::chrono::time_point<std::chrono::system_clock> start, end;
;

还是依照以前的习惯,写好时间计算类

3、定义数据结构

struct s_data

	uint8_t* data = NULL;
	size_t len = 0;
	s_data(const char* in)
	
		len = strlen(in);
		data = (uint8_t*)malloc(len+1);
		if (data == NULL)
			return;
		memcpy(data, in, len);
		data[len] = '\\0';
	
	~s_data()
	
		if (data != NULL)
			free(data);
	
;
typedef std::shared_ptr<s_data> ptr_s_data;

我们之所以使用智能指针,是希望程序直接释放,在不需要的时候直接内存放弃

4、boost 无锁队列

boost 无锁数据结构:以下摘自于boost文档
boost.lockfree implements three lock-free data structures:

boost::lockfree::queue
a lock-free multi-produced/multi-consumer queue

boost::lockfree::stack
a lock-free multi-produced/multi-consumer stack

boost::lockfree::spsc_queue
a wait-free single-producer/single-consumer queue (commonly known as ringbuffer)

下面我们使用spsc_queue

boost::lockfree::spsc_queue<ptr_s_data, boost::lockfree::capacity<100> > spsc_queue;

1、这是个单生产者,单消费者,多生产者,多消费者用处比较少,这里不使用,且以性能来说,spsc_queue的性能要高。高性能的程序不在于线程使用很多,而在于使用线程和内存以及cpu,gpu的合理性。
2、里面的100是指的程序缓冲,但值得注意的是:这个缓冲我们使用的是指针,而不是真实的数据空间。

使用指针空间意味着内存在外分配,我们成为带外分配,也就意味着内存容易撕裂。那我们应该怎么正确分配内存?一般来说,程序都会使用自己的内存管理,分配一个较大的内存,从中取得内存使用权限,有一个再分配管理。

int main()

	if (spsc_queue.is_lock_free())
		std::cout << "single producer, single consume" << std::endl;
	//1000 second;// 000
	TicToc tp;
	std::thread thread_producer([=,&tp]() 
		//every ten millonsecond to 
		int insert_num = 0;
		for (;;)
		
			if (spsc_queue.write_available())
			
				char buffer[128];
				sprintf(buffer, "this is test %d\\n", insert_num);
				ptr_s_data obj = std::make_shared<s_data>(buffer);
				spsc_queue.push(obj);
				if (insert_num++ > 100000)
					break;
				//condition_producers.notify_one();
			
			else
				boost::this_thread::sleep(1);
		
		
		
		std::cout << tp.toc()<< " the insert is over " << std::endl;

	);

	std::thread thread_consume([]() 

		TicToc tc;
		while (!done)
		
			std::unique_lock<std::mutex> ul(pro_mtx);
			//condition_producers.wait(ul);// , [] 
			if (spsc_queue.read_available() > 0)
			
				ptr_s_data obj = nullptr;
				spsc_queue.pop(obj);
				ncount++;
				if (obj && obj->data != NULL)
				
					if ((ncount % 10000) == 0)
						std::cout << obj->data;
				
			
			else
				lee_mill(2);
		
		std::cout << "left is " << spsc_queue.read_available() << std::endl;
		std::cout << tc.toc() << " consume is over" << std::endl;
	);

	thread_producer.join();
	done = true;
	thread_consume.join();

	return 1;

以下是执行的结果,看样子left是零,不过是巧合,生产者线程和消费者线程比较均衡。所以left 多少是与cpu 线程执行有关。剩余100 个,也是有可能的。读者可以自己调整,让其没有剩余,就是在退出循环之后,继续把剩余的执行完毕,然我们的目的不是这个,主要是为了哟用自己的内存管理单元,我们称之为mmu
上面的程序是直接分配内存,删除内存,并没有内存管理,那么如果做到内存管理呢,我们必须制作自己的mmu。

5、mmu内存管理单元

这是我前面的内存池文章
上面这篇文章讨论了内存池的制作,我们使用mmu,就是要在内存池上更上一个台阶,能够达到管理的作用,这个下一节再讲了

以上是关于c++千万数据级别正确使用无锁队列,避免内存撕碎的主要内容,如果未能解决你的问题,请参考以下文章

C++ 无锁队列与多线程崩溃

无锁队列真的比有锁队列快吗c++ linux后台开发

c++内存池无锁

c++内存池无锁

folly无锁队列正确性说明

基于共享内存的无锁消息队列设计