CAS 无锁队列

best-farmer 2019-03-01 原文

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发生抢占,所以要先进行比较,如果一样再进行操作,这样就能保证一致性。

发表于 2019-03-01 17:47 shininglight 阅读() 评论() 编辑 收藏

 

版权声明:本文为best-farmer原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/best-farmer/p/10457871.html

CAS 无锁队列的更多相关文章

  1. 利用Python requests库实现cas认证

    利用Python requests库实现cas认证 1.准备工作-背景知识 1.1 requests库简介: […]...

  2. Nginx(八): 观进程锁的实现

      前面的nginx系列讲解了nginx很多通用概念,流程,以及核心的http模块的一些实现。应该说大体上对n […]...

  3. Java中Atomic类的使用分析

    1:为什么会出现Atomic类   在多线程或者并发环境中,我们常常会遇到这种情况 int i=0; i++ […]...

  4. (七)CAS 本地localhost调试,无法单点退出疑问

                    干活的时候要多思考——– 题记       […]...

  5. CAS 之 Hello World(二)

    CAS 之 Hello World(二) 标签(空格分隔): CAS Intro(介绍) 由上节可知Apere […]...

  6. 我们常说的 CAS 自旋锁是什么

    CAS(Compare and swap),即比较并交换,也是实现我们平时所说的自旋锁或乐观锁的核心操作。 它 […]...

  7. CAS 和 ABA 问题

    CAS简介 CAS 全称是 compare and swap,是一种用于在多线程环境下实现同步功能的机制。 C […]...

  8. Java并发/多线程-CAS原理分析

    目录 什么是CAS 并发安全问题 举一个典型的例子i++ 如何解决? 底层原理 CAS需要注意的问题 使用限制 […]...

随机推荐

  1. 【Intellij 】Intellij IDEA 添加jar包的三种方式

    一.直接复制:(不推荐) 方法:直接将硬盘上的jar包复制粘贴到项目的lib目录下即可。 注意: 1.对于导入 […]...

  2. go基础系列:结构struct

    Go语言不是一门面向对象的语言,没有对象和继承,也没有面向对象的多态、重写相关特性。 Go所拥有的是数据结构, […]...

  3. [python应用]python简单图片抓取

    前言 emmmm python简单图片抓取   1 import requests 2 import thre […]...

  4. 14.Django基础之jQuery操作cookie

    jquery之cookie操作   定义:让网站服务器把少量数据储存到客户端的硬盘或内存,从客户端的硬盘读取数 […]...

  5. haproxy实现会话保持(2):stick table

    <!– /*! normalize.css v3.0.1 | MIT License | g […]...

  6. 国内外前端(js)开发框架对比

    国内外前端开发框架对比 首先我们先对目前国内外主流前端开发框架做一个基本的了解,之后再对他们进行一个直观的对比 […]...

  7. JQuery 遍历的方法

    jQuery parent() 方法parent() 方法返回被选元素的直接父元素。该方法只会向上一级对 DOM 树进行遍历。下面的例子返回每个 元素的直接父元素:实例$(document).ready(functio...

  8. 基于腾讯云的视频聊天研究

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 作者:欧彦兴 简介 最近有个需求是与视频聊天相关, […]...

展开目录

目录导航