CAS 无锁队列

队列是常用的数据结构,采用的FIFO(first in firstout)原则,新元素(等待进入队列的元素)总是被插入到尾部,而读取的时候总是从头部开始读取。在计算中队列一般用来做排队(如线程池的等待排队,锁的等待排队),用来做解耦(生产者消费者模式),异步等等。在java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列。队列在现实生活中也很常见,例如去超市买东西排队付钱,先来的先付账走人回家,后来的要等前面的人付完钱才能付账。

首先我们看一段队列代码:

type Queue struct {
    head  unsafe.Pointer
    tail     unsafe.Pointer
    Reset  func(interface{})
    New    func() interface{}
}

// one node in queue
type Node struct {
    val  interface{}
    next unsafe.Pointer
}

func QueueNew()(*Queue){
    queue := new(Queue)
    queue.head =  unsafe.Pointer(new(Node))
    queue.tail = queue.head
    return queue
}

func (self *Queue) EnQueue(val interface{}) {

    if self.Reset!= nil{
        self.Reset(val)
    }
    newNode := unsafe.Pointer(&Node{val: val, next: nil})
    var tail, next unsafe.Pointer
    tail = self.tail
    ((*Node)(tail)).next = newNode
    self.tail = newNode
}

func (self *Queue) DeQueue() (val interface{}) {
    var head, tail, next unsafe.Pointer
    head = self.head
    tail = self.tail
    next = ((*Node)(head)).next
    if head == tail {
        // There‘s no data in the queue.
        return nil
    }
    val = ((*Node)(next)).val
    self.head = next
    return val
}

这是一般的队列实现方法,适用于单线程但如果是多线程操作就麻烦了。例如在超市柜台结账,大家都按规则进行排队没有问题,但是如果有两个人张大妈和李大妈都着急结账回家接孙子,同时跑到了同一个队列的队尾,她们两都说自己应该排在队尾。那么问题就来了。那么对于多线程操作同一个队列,可以用锁的方法来实现,在入队和出队前加上锁即可:

type Queue struct {
    sync.RWMutex
    head unsafe.Pointer
    tail unsafe.Pointer
    Reset func(interface{})
    New func() interface{}
}

func (self *Queue) EnQueue(val interface{}) {
    self.Lock()
    defer self.Unlock()

    if self.Reset != nil {
        self.Reset(val)
    }
    newNode := unsafe.Pointer(&Node{val: val, next: nil})
    var tail, next unsafe.Pointer
    tail = self.tail
    ((*Node)(tail)).next = newNode
    self.tail = newNode
}

func (self *Queue) DeQueue() (val interface{}) {
    var head, tail, next unsafe.Pointer

    self.Lock()
    defer self.Unlock()

    head = self.head
    tail = self.tail
    next = ((*Node)(head)).next
    if head == tail {
        // There‘s no data in the queue.
        return nil
    }
    val = ((*Node)(next)).val
    self.head = next
    return val
}

但是,这种加锁的方法在多进程的操作中会消耗很多系统资源,使用不当还会造成死锁,下面推荐一种CAS的方法来实现队列的安全出队和入队。CAS(Compare and Swap),比较并交换,在大多数处理器架构,CAS的具体是判断一个内存上的数据是否是所判断的值,如果是,那么执行修改;如果不是,那么将不做操作并返回当前值。CAS是一种乐观锁,多线程执行过程中,多个线程去修改内存中的数据,有且只有一个能修改成功,但是失败的线程不会中断或者挂起。具体代码如下:

func (self *Queue) EnQueue(val interface{}) {

	if self.Reset!= nil{
		self.Reset(val)
	}
	newNode := unsafe.Pointer(&Node{val: val, next: nil})
	var tail, next unsafe.Pointer
	for {
		tail = self.tail
		next = ((*Node)(tail)).next
		if tail != self.tail{
			runtime.Gosched()
			continue
		}                //[PositionA]-----------A new node may already enqueue-------------
		if next != nil {
			atomic.CompareAndSwapPointer(&(self.tail), tail, next)
			continue
		}
		if atomic.CompareAndSwapPointer(&((*Node)(tail).next), nil,newNode ) {
		    break
		}
		runtime.Gosched()
	}
	atomic.CompareAndSwapPointer(&(self.tail),tail, newNode)
}

func (self *Queue) DeQueue() (val interface{}) {
	var head, tail, next unsafe.Pointer
	for {
		head = self.head
		tail = self.tail
		next = ((*Node)(head)).next
		if head != self.head{
			runtime.Gosched()
			continue
		}
		if next == nil{
			if self.New != nil{
				return self.New()
			}else{
				return nil
			}

		}
		if head == tail {
			atomic.CompareAndSwapPointer(&(self.tail), tail, next)
		}else{
			val = ((*Node)(next)).val                        //[PositionB]---------The head node may already Dequeue---------
			if atomic.CompareAndSwapPointer(&(self.head), head, next) {
				return val
		    	}
		}
		runtime.Gosched()
	}
}

  多线程在运行这段代码的过程中可能在位置A和位置B发生抢占,所以要先进行比较,如果一样再进行操作,这样就能保证一致性。

原文地址:https://www.cnblogs.com/best-farmer/p/10457871.html

时间: 2024-08-03 09:10:36

CAS 无锁队列的相关文章

转载:无锁队列的实现(CAS同步)

转自:http://coolshell.cn/articles/8239.html 关于无锁队列的实现,网上有很多文章,虽然本文可能和那些文章有所重复,但是我还是想以我自己的方式把这些文章中的重要的知识点串起来和大家讲一讲这个技术.下面开始正文. 关于CAS等原子操作 在开始说无锁队列之前,我们需要知道一个很重要的技术就是CAS操作——Compare & Set,或是 Compare & Swap,现在几乎所有的CPU指令都支持CAS的原子操作,X86下对应的是 CMPXCHG 汇编指令.

锁、CAS操作和无锁队列的实现

https://blog.csdn.net/yishizuofei/article/details/78353722 锁的机制 锁和人很像,有的人乐观,总会想到好的一方面,所以只要越努力,就会越幸运:有的人悲观,总会想到不好的一方面,患得患失,所以经常会做不好事.我一直把前一个当作为我前进的动力和方向,快乐充实的过好每一天. 常用的锁机制也有两种: 1.乐观锁:假设不会发生并发冲突,每次不加锁而去完成某项操作,只在提交操作时,检查是否违反数据完整性.如果因为冲突失败就继续重试,直到成功为止.而乐

基于循环数组的无锁队列

在之前的两篇博客(线程安全的无锁RingBuffer的实现,多个写线程一个读线程的无锁队列实现)中,分别写了在只有一个读线程.一个写线程的情况下,以及只有一个写线程.两个读线程的情况下,不采用加锁技术,甚至原子运算的循环队列的实现.但是,在其他的情况下,我们也需要尽可能高效的线程安全的队列的实现.本文实现了一种基于循环数组和原子运算的无锁队列.采用原子运算(compare and swap)而不是加锁同步,可以很大的提高运行效率.之所以用循环数组,是因为这样在使用过程中不需要反复开辟内存空间,可

环形无锁队列

环形无锁队列 Table of Contents 1 环形无锁队列的实现 2 死锁及饥饿 3 一些优化 1 环形无锁队列的实现 数据结构定义: template class LockFreeQueue { private: ElementT *mArray; int mCapacity; int mFront; int mTail; } 由于出队操作是在队首进行,入队操作是在队尾进行,因此,我们可以尝试用mFront和mTail来实现多个线程之间的协调.这其中会用到CAS操作: 入队操作伪码:

无锁队列的环形数组实现

对无锁队列的最初兴趣来自梁斌同志的一个英雄帖:http://coderpk.com/. 第一次看到这个题目的时候还不知道CAS,FAA等所谓的“原子操作”,但直觉上感觉,通过对读写操作的性能优化来达到大幅提高队列性能的方法是行不通的,就算读写操作全用汇编来写,也不会和正常的read及 write有数量级上的区别.后来搜索了一下lock free data structure,才知道了关于原子操作的一些东西,同时也纠正了自己由来已久的一个错误观点:C++中的++操作和--操作都不是原子操作.这篇笔

多线程编程之无锁队列

关于无锁队列的概念与实现,可以参考博文<无锁队列的实现>,主要涉及到的知识点包括CAS原子操作.无锁队列的链表实现.无锁队列的数组实现以及ABA问题. 下面借鉴了<多线程的那点儿事(之无锁队列)>的代码,说明两个线程(一个添加一个读取数据)之间的无锁队列,可以不借助线程互斥方法就能够达到并行效果.代码如下: #define MAX_NUMBER 1000L #define STATUS int #define OK 0 #define FALSE -1 typedef struct

boost 无锁队列

一哥们翻译的boost的无锁队列的官方文档 原文地址:http://blog.csdn.net/great3779/article/details/8765103 Boost_1_53_0终于迎来了久违的Boost.Lockfree模块,本着学习的心态,将其翻译如下.(原文地址:http://www.boost.org/doc/libs/1_53_0/doc/html/lockfree.html) Chapter 17. Boost.Lockfree 第17章.Boost.Lockfree Ta

并发无锁队列

1.前言 队列在计算机中非常重要的一种数据结构,尤其在操作系统中.队列典型的特征是先进先出(FIFO),符合流水线业务流程.在进程间通信.网络通信之间经常采用队列做缓存,缓解数据处理压力.结合自己在工作中遇到的队列问题,总结一下对不同场景下的队列实现.根据操作队列的场景分为:单生产者--单消费者.多生产者--单消费者.单生产者--多消费者.多生产者--多消费者四大模型.其实后面三种的队列,可以归纳为一种多对多.根据队列中数据分为:队列中的数据是定长的.队列中的数据是变长的. 2.队列操作模型 (

基于无锁队列和c++11的高性能线程池

基于无锁队列和c++11的高性能线程池线程使用c++11库和线程池之间的消息通讯使用一个简单的无锁消息队列适用于linux平台,gcc 4.6以上 标签: <无> 代码片段(6)[全屏查看所有代码] 1. [代码]lckfree.h ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 // lckfree.h //