
AI
push与pop接口若不指定第二个参数,默认会阻塞,使用时需留意这一情况。以下为CircularQueue类的单元测试示例代码:单元测试的结果:
上面的循环队列通过锁确保了线程安全。下面是用C++11实现的环形队列,线程安全且无锁,阻塞读、非阻塞读以及写操作均被支持。这段代码构建了一个基于原子操作的环形队列。在其Push与Pop方法里,写入时运用memory_order_release,读取时采用memory_order_acquire,以此确保数据的可见性、内存屏障以及顺序性,进而规避多线程并发引发的问题。针对等待状况,使用线程yield的方法,避免CPU空转。而且,Pop方法具备函数式语法形式,能借助lambda表达式对弹出元素进行加工。最后还给出了异步弹出接口PopAsync,它接收回调函数callback,在弹出操作完成时调用该回调函数。总体而言,该代码较为完整,可读性也不错,不过在使用时,还得依据实际业务场景与需求做进一步优化与改进。不过要留意,这仅能达成一读一写的线程安全,若有多个读者或者多个写者时就会线程不安全了。理解几个内存时序是无锁编程的难点所在。对内存时序操作的解释进行补充。C++定义了多种内存时序,这些时序明确了原子变量前后全部内存操作(普通变量、原子变量都包含)的排序方式。std::memory_order_relaxed仅确保操作的原子性,对同一原子变量的多个操作没有任何内存序的限制。这意味着这些操作能任意重排,还可随时被其他线程的操作干扰。所以,在使用std::memory_order_relaxed时得格外谨慎,要保证操作的正确性不会被这种松散的内存访问顺序影响。std::memory_order_relaxed大多用在无需任何同步机制之处,像计数器的自增、自减操作等。这类操作只需确保结果正确,不用保障执行顺序。所以,std::memory_order_relaxed是速度最快的内存序,不过也是最具危险性的一种内存序。std::memory_order_acquire会保证先完成之前的所有读操作,再执行当前读操作。也就是说,若当前读操作要用到之前读操作的结果,就能正确获取这些结果。具体而言,在采用memory_order_acquire语义的情况下,编译器与处理器都会确保当前线程所在的CPU核心(或者处理器)在执行当下原子操作之前,先把之前所有读操作获取的数据从CPU缓存刷新到主内存,这样就能保证当前线程获取到其他线程对共享变量的最新修改。memory_order_acquire语义能确保程序正确,防止数据竞争。不过,它或许会使程序性能下降。由于在执行原子操作前,要把之前的所有读操作都刷新到主内存,这可能增加缓存一致性协议的开销。所以,在实际编程时,需依据具体情形选用恰当的内存序语义。std::memory_order_release能保证在该原子操作前,当前线程的所有写操作均已完成,且这些写操作对其他线程可见。如此一来,其他线程就能看到当前线程对共享数据作出的改动。这种释放操作常被用于同步操作,像更新共享变量的值后通知其他线程时就会用到。在这种情形下,std::memory_order_release能保证其他线程看到更新后的值。Push有左值和右值两个插入版本,能提升一点C++性能。PushImpl提取公共代码实现复用,这种做法很优雅。Pop有两个版本,其中一个变体不返回pop出的对象,而是调用外部传入的回调来操作pop出的对象。同样提供单元测试。测试得出的结果:
Copyright © 2025 IZhiDa.com All Rights Reserved.
知答 版权所有 粤ICP备2023042255号