栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++ 无锁队列

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++ 无锁队列

C++ 无锁队列
  • 一、atomic原子类型
  • 二、无锁队列
    • 1.代码实现
    • 2. 多线程读写
  • 三、总结


一、atomic原子类型

atomic类型的操作是原子操作,是不可分割的,不能被中断的操作。
程序代码中的一条简单赋值语句会被翻译为多条汇编指令,那么多个线程同时对某一存储单元进行修改,就有可能出现脏数据,原子操作可以避免脏数据的出现。

二、无锁队列 1.代码实现
#include 
#include 
#include 
#include 

using namespace std;

static const int QUEUE_SIZE = 1024;

static const int PRODUCES_NUMBER = 3;
static const int CONSUMERS_NUMBER = 4;
static const int MSG_NUMBER = 200;

template
struct Node
{
	T data;	//数据
	atomic_bool hasData; //是否有数据
};

template
class Queue
{
public:
	Queue():msg(QUEUE_SIZE)
	{
		r_index = 0;
		w_index = 0;
	};
	bool enqueue(T& value);  // 入队列
	bool dequeue(T& value);  // 出队列

private:
	vector> msg;	// 消息队列
	atomic r_index; // 读索引(不考虑溢出情况)
	atomic w_index; // 写索引(不考虑溢出情况)
};

template
bool Queue::enqueue(T& value)
{
	size_t l_w_index = w_index.load(std::memory_order_relaxed);
	Node* node = NULL;

	// CAS比较是否和预期一致,一致则对写索引递增1,不一致则重试
	do
	{
		//队列是否已满
		if (l_w_index >= r_index.load(std::memory_order_relaxed) + msg.size()) {
			return false;
		}

		// 判断是否有数据
		size_t index = l_w_index % msg.size();
		node = &msg[index];
		if (node->hasData.load(std::memory_order_relaxed))
		{
			return false;
		}
	} while (!w_index.compare_exchange_weak(l_w_index, l_w_index + 1, std::memory_order_relaxed));

	//写数据
	node->data = std::move(value);//左值转右值,避免拷贝带来的浪费
	node->hasData.store(true);

	return true;
}

template
bool Queue::dequeue(T& value)
{
	size_t l_r_index = r_index.load(std::memory_order_relaxed);;
	Node* node = NULL;

	// CAS比较是否和预期一致,一致则对读索引递增1,不一致则重试
	do
	{
		//队列是否为空
		if (l_r_index > w_index.load(std::memory_order_relaxed))
		{
			return false;
		}

		// 判断是否有数据
		size_t index = l_r_index % msg.size();
		node = &msg[index];
		if (!node->hasData.load(std::memory_order_relaxed))
		{
			return false;
		}
	} while (!r_index.compare_exchange_weak(l_r_index, l_r_index + 1, std::memory_order_relaxed));

	//读数据
	value = std::move(node->data);
	node->hasData.store(false);

	return true;
}
2. 多线程读写
int main()
{
	//控制线程
	std::atomic producer_thread_count = 0;
	std::atomic consumer_thread_count = 0;

	//队列
	Queue queue;
	//消息序列
	std::atomic sequence = 0;
	//生产者线程
	auto producer = [&queue, &sequence, &producer_thread_count]() {
		for (uint32_t i = 0; i < MSG_NUMBER; i++)
		{
			uint32_t num = sequence++;
			while(!queue.enqueue(num));//入队列[0, (MSG_NUMBER-1) * PRODUCES_NUMBER)
		}
		producer_thread_count++;
	};

	//消费者线程
	std::atomic counter[MSG_NUMBER * PRODUCES_NUMBER];
	auto consumer = [&queue, &counter,&consumer_thread_count]() {
		uint32_t num = 0;
		while (queue.dequeue(num))
		{
			counter[num]++;//出队列后把对应索引位的值递增1
		}
		consumer_thread_count++;
	};

	//线程池
	std::unique_ptr produce_threads[PRODUCES_NUMBER];
	std::unique_ptr consumer_threads[CONSUMERS_NUMBER];

	//创建线程
	for (int i = 0; i < PRODUCES_NUMBER; i++)
	{
		produce_threads[i].reset(new std::thread(producer));
		produce_threads[i]->detach();
	}
	for (int i = 0; i < CONSUMERS_NUMBER; i++)
	{
		consumer_threads[i].reset(new std::thread(consumer));
		consumer_threads[i]->detach();
	}

	while (producer_thread_count != PRODUCES_NUMBER || consumer_thread_count != CONSUMERS_NUMBER)
	{
		std::this_thread::sleep_for(std::chrono::seconds(5));
	}

	//判断是否有竞争
	for (int i = 0; i < (MSG_NUMBER*PRODUCES_NUMBER); i++)
	{
		if (counter[i] != 1)
		{
			std::cout << "found race conditiont" << i << 't' << counter[i] << std::endl;
			break;
		}
	}

	return 0;
}

三、总结

无锁队列依靠原子和CAS操作,对队列的读写索引进行判断来入队和出队,它没有使用互斥量mutex来进行加锁,从性能上具有明显的优势,但同时编程的复杂性增加了很多,在编码时也要对内存序有简单的了解。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1000902.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号