Lock Free Stack

本文深入解析Java并发编程中的锁自由队列概念,指出常见实现中的错误,并提供正确的代码实现。重点阐述了在多生产者多消费者场景下,如何确保线程安全地更新指针,以及在插入节点时需要处理的复杂情况。
This example is copied from book "Java Concurrency in Practice".
From this example we can learn that the lock free queue code is not correct for MPMC (multi-producer-multi-consumer) in my previous post.

Actually, lock free means you should guarantee the atomicity of updating a pointer. When you add a new node on top of current stack top, the top node might be poped by another thread. So you should prepare to do that again if that did happen. How to do this? We thus need a CAS operation, if current top is what we saw and we have successfully update the top to a new node, ok, our job is done.

While, lock free queue can be more complicated if you insert one node to the tail since we need to update two pointers at same time, one is a *tail* pointing to the last node and one is its *next* pointer.

I will give one correct queue code in the next post.


@ThreadSafe
public class ConcurrentStack <E> {
AtomicReference<Node<E>> top = new AtomicReference<Node<E>>();
public void push(E item) {
Node<E> newHead = new Node<E>(item);
Node<E> oldHead;
do {
oldHead = top.get();
newHead.next = oldHead;
} while (!top.compareAndSet(oldHead, newHead));
}
public E pop() {
Node<E> oldHead;
Node<E> newHead;
do {
oldHead = top.get();
if (oldHead == null)
return null;
newHead = oldHead.next;
} while (!top.compareAndSet(oldHead, newHead));
return oldHead.item;
}
private static class Node <E> {
public final E item;
public Node<E> next;
public Node(E item) {
this.item = item;
}
}
}
<think>嗯,用户在使用boost lockfree queue存放自定义的Packet结构体指针时遇到了断言失败的问题,具体是has_capacity这个断言失败。我需要仔细分析可能的原因,并给出解决方案。 首先,boost的lockfree队列有三种类型:spsc_queue、queue和stack。其中spsc_queue(单生产者单消费者)在编译时需要指定容量,并且容量必须是2的幂。如果用户使用的队列类型是spsc_queue,那么初始化时传入的容量如果不是2的幂,就会触发断言失败。例如,用户可能设置了容量为10,但实际需要的是16这样的2的幂次方数。这时候需要检查队列初始化时的容量参数是否正确。 其次,用户存放的是自定义结构体指针,也就是Packet*。lockfree队列要求元素必须是trivially copyable类型,也就是说,使用memcpy复制是安全的。指针类型本身是trivially copyable的,所以这里可能没问题。但如果用户的结构体内部有非trivially copyable的成员,比如动态内存指针,可能会引发问题。不过根据描述,用户的结构体包含std::array<char, size_t>,这里可能存在语法错误,正确的应该是std::array<char, N>,其中N是固定大小。如果data_length是动态的,可能结构体设计有问题,导致无法正确复制。需要确认结构体的定义是否符合trivially copyable的要求。 另外,多线程环境下的生产者和消费者数量是否符合队列类型的要求。例如,spsc_queue只能有一个生产者和一个消费者。如果用户有多个生产者或消费者,就需要使用queue或stack类型,但这些类型可能需要原子操作支持,如果平台不支持就会导致问题。此外,队列的push和pop操作是否在正确的线程上下文中执行也需要检查。 内存管理方面,用户使用指针队列时需要注意内存的生命周期。如果生产者在push指针后,消费者还没有pop之前就释放了内存,会导致悬垂指针。这种情况下,可能需要使用智能指针或其他内存管理机制来确保指针的有效性。例如,使用shared_ptr或自定义的内存池来管理Packet对象的生命周期。 代码示例方面,用户的结构体可能定义有误。比如,std::array<char, size_t>中的size_t作为模板参数是不允许的,因为模板参数必须是编译期常量。正确的做法是使用固定大小的数组,或者改用指针和长度字段。例如,结构体可以改为包含一个char*指针和data_length字段,但这样需要确保内存的正确分配和释放。 总结可能的原因: 1. 队列容量未满足2的幂次方(针对spsc_queue)。 2. 结构体不符合trivially copyable要求。 3. 多线程环境下生产者/消费者数量超过队列类型限制。 4. 内存管理问题导致指针失效。 5. 结构体定义中存在语法或设计错误,如动态大小的std::array。 针对这些问题,解决方案包括: - 检查队列初始化时的容量是否为2的幂。 - 确保结构体是trivially copyable,可以使用static_assert验证。 - 确认队列类型与生产消费模型匹配。 - 使用智能指针管理对象生命周期。 - 修正结构体定义中的错误,如使用固定大小的数组或正确管理动态内存。 另外,参考用户提供的引用内容,例如关于RING BUFFER的写指针管理[^2],可能需要确保队列的同步机制正确,避免竞争条件。例如,检查sync_seq或其他同步变量的使用是否符合预期。 最后,建议用户提供更多代码细节,如队列声明、结构体定义和操作代码,以便更准确地定位问题。</think>### 使用boost lockfree queue存放自定义指针时断言失败问题分析 #### 1. 队列容量初始化问题 - **断言`has_capacity`失败**通常与`boost::lockfree::spsc_queue`的容量约束相关。该队列要求容量必须是2的幂次方,例如256、512等[^2]。 - **解决方案**:检查初始化代码,确保容量参数为2的幂次方。例如: ```cpp boost::lockfree::spsc_queue<Packet*, boost::lockfree::capacity<256>> queue; ``` #### 2. 数据类型兼容性验证 - `lockfree`队列要求元素类型是`trivially copyable`(可通过`memcpy`安全复制)。指针类型(如`Packet*`)本身满足条件,但需验证结构体定义: ```cpp struct Packet { uint16_t id; std::array<char, 1024> data; // 必须使用固定大小,例如1024 size_t data_length; }; static_assert(std::is_trivially_copyable<Packet>::value, "类型不兼容"); ``` #### 3. 线程模型匹配性检查 - `spsc_queue`仅支持**单生产者单消费者**模型。若存在多生产者或多消费者,需改用`boost::lockfree::queue`: ```cpp boost::lockfree::queue<Packet*> queue(256); // 多生产者多消费者 ``` #### 4. 内存生命周期管理 - 使用指针队列时,需确保对象在消费完成前不被释放。建议使用`std::shared_ptr`: ```cpp boost::lockfree::spsc_queue<std::shared_ptr<Packet>, boost::lockfree::capacity<256>> queue; ``` #### 5. 完整代码示例修正 ```cpp #include <boost/lockfree/spsc_queue.hpp> #include <memory> struct Packet { uint16_t id; std::array<char, 1024> data; // 固定大小数组 size_t data_length; }; static_assert(std::is_trivially_copyable<Packet>::value, "Packet must be trivially copyable"); int main() { boost::lockfree::spsc_queue<Packet*, boost::lockfree::capacity<256>> queue; Packet* pkt = new Packet{1, {}, 128}; queue.push(pkt); // 生产者线程操作 // 消费者线程中: Packet* received; if (queue.pop(received)) { // 处理数据后需手动释放内存 delete received; } return 0; } ``` #### 引用说明 - 关于环形缓冲区(RING BUFFER)的同步机制,可参考`sync_seq`通过取模运算管理写指针的设计[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值