无锁环形缓冲(lock-free ring buffer)是一种数据结构,用于在多线程环境下实现高效的数据传递,而无需使用传统的锁机制。它通常用于生产者-消费者模型,其中多个线程同时访问共享的缓冲区。
主要优点:
- 环形缓冲区: 环形结构避免了数据的拷贝和移动。
- 无锁设计:运用原子操作,实现无锁,避免每一次使用时的加锁的消耗。
实现的一些基本操作:
- 初始化: 初始化缓冲区及其相关的控制变量。
- 入队(生产者): 将数据放入缓冲区。
- 出队(消费者): 从缓冲区取出数据。
- 判空和判满: 检查缓冲区是否为空或已满。
- 动态扩展: 在需要时动态扩展缓冲区的大小。
原子操作用于确保操作的原子性,即操作要么全部执行,要么不执行
内存顺序是指对于原子操作,确定了这些操作对内存的读写的顺序。不同的内存顺序规定了不同的可见性和一致性保证。
memory_order_relaxed: 最宽松的顺序,允许编译器和处理器对操作进行某些重排序。
memory_order_acquire: 保证当前线程读取的数据是最新的,防止读取到旧数据。
memory_order_release: 保证当前线程的写入对其他线程可见,防止写入操作被重排序到后面的读取操作之前。
memory_order_acq_rel: 既包含 acquire 的语义,也包含 release 的语义,适用于读取和写入都存在的操作。
memory_order_seq_cst: 最严格的顺序,所有操作按照全局的全序关系进行,提供了最强的一致性保证。
这里我没有自己造轮子,使用的是 https://github.com/jnk0le/Ring-Buffer这里实现的ringbuffer。
链接里的ringbuff对insert、remove等函数进行了重载,实现可以移除一定数量的元素,移除一个数量并将其返回等等功能,功能非常丰富,下面对其的使用进行简单说明。
jnk0le::Ringbuffer<const char*, 256> message;
int main()
{
//...
while(1)
{
const char* tmp = nullptr;
while(!message.remove(tmp));
printf("%s fired\n", tmp);
//...
}
}
extern "C" void SysTick_Handler(void)
{
message.insert("SysTick_Handler");
}
-
jnk0le::Ringbuffer<const char*, 256> message;
: 声明了一个名为message
的环形缓冲,其中存储了const char*
类型的数据,最大容量为 256 个元素。 -
while(1) {...}
: 一个无限循环,用于在主程序中不断地从环形缓冲中取出消息并进行处理。 -
const char* tmp = nullptr;
: 声明一个指向const char*
的临时变量,并初始化为nullptr
。 -
while(!message.remove(tmp));
: 在环形缓冲中循环等待,直到成功从缓冲中取出消息,并将其存储在tmp
变量中。 -
printf("%s fired\n", tmp);
: 打印出被取出的消息。 -
extern "C" void SysTick_Handler(void) {...}
: 定义了一个外部的SysTick_Handler
中断处理函数。在这个例子中,该中断处理函数向环形缓冲中插入消息 “SysTick_Handler”。