首页 > 其他分享 >golang简单实现CLHLock,不可重入的clh自旋锁

golang简单实现CLHLock,不可重入的clh自旋锁

时间:2023-08-14 10:57:00浏览次数:50  
标签:node CLHLock clh lock iclhNode golang tail func

如果不想自旋,可以把Lock、waitIsFinish和noticeIsFinish代码中的方式2注释掉,改用方式1。不过实际测试在低并发的情况下,自旋的执行效率更高,要根据实际业务场景选择使用哪种方式。

源代码如下:

import (
    "runtime"
    "sync/atomic"
)

const (
    Gosched_Spin_Count = 100000 // 自旋多少次执行一次Gosched
)

// 基于clh原理实现的锁,主要是为了保证按照获取锁顺序排队,使用自旋锁适合低并发场景
type CLHLock struct {
    tail atomic.Value // *CLHNode
}

type ICLHLock interface {
    Lock() (iclhNode, bool)
    Unlock(iclhNode)
}

var nilNode iclhNode = &clhNode{}

func NewCLHLock() ICLHLock {
    lock := &CLHLock{}
    lock.tail.Store(nilNode)
    return lock
}

type clhNode struct {
    // isFinishCh chan struct{}
    isFinish   bool
}

type iclhNode interface {
    waitIsFinish()
    noticeIsFinish()
}

func (lock *CLHLock) Lock() (iclhNode, bool) {
    newNode := &clhNode{
        // isFinishCh: make(chan int, 1),
        isFinish: false,
    }
    hasPreNode := false

    // 将tail指向新节点
    prevNode, _ := lock.tail.Swap(newNode).(iclhNode)

    // 如果存在前驱节点,就在前驱节点释放锁之前自旋等待
    if prevNode != nilNode {
        hasPreNode = true
        // 等待前驱节点释放锁
        prevNode.waitIsFinish()
    }
    return newNode, hasPreNode
}

func (lock *CLHLock) Unlock(node iclhNode) {
    if node == nil {
        return
    }
    // 尝试将tail设置为nil,表示当前goroutine释放了锁
    lock.tail.CompareAndSwap(node, nilNode)
    // 如果设置tail为nil失败,表示有等待锁的后继节点,
    // 所以直接将当前节点的锁标志位设置为false并放弃修改tail。
    node.noticeIsFinish()
}

func (node *clhNode) waitIsFinish() {
    // 方式1 ch
    // <-node.isFinishCh
    // 方式2 自旋
    for i := 0; ; i++ {
        if node.isFinish {
            break
        }
        if i > Gosched_Spin_Count {
            runtime.Gosched()
            i = 0
        }
    }
}

func (node *clhNode) noticeIsFinish() {
    // 方式1 ch
    // node.isFinishCh <- struct{}{}
    // close(node.isFinishCh)
    // 方式2 自旋
    node.isFinish = true
}

测试demo

var counter int
var wg sync.WaitGroup

func testLock(clh myutil.ICLHLock, i int) {
    defer wg.Done()

    for j := 0; j < 100000; j++ {
        lockNode, _ := clh.Lock()
        counter++
        clh.Unlock(lockNode)
    }
}

func main() {
    // Adjust the number of goroutines for your desired concurrency level
    numGoroutines := 10
    // Create an instance of your CLH lock
    clh := myutil.NewCLHLock() // Replace this with your CLH lock initialization
    // Test : Measure performance under high concurrency
    fmt.Println("Test : Performance under High Concurrency")
    fmt.Println("========================================")
    counter = 0
    wg.Add(numGoroutines)
    startTime := time.Now()
    for i := 0; i < numGoroutines; i++ {
        go testLock(clh, i)
    }
    wg.Wait()
    endTime := time.Now()
    fmt.Printf("Counter value: %d\n", counter)
    fmt.Printf("Time taken for the test: %s\n", endTime.Sub(startTime))
    fmt.Println("\nTesting completed.")
}

 

 

标签:node,CLHLock,clh,lock,iclhNode,golang,tail,func
From: https://www.cnblogs.com/feng-gamer/p/17628043.html

相关文章

  • Golang之旅——内存管理
    转载放在最前一文带你了解,虚拟内存、内存分页、分段、段页式内存管理[Golang三关-典藏版]一站式Golang内存洗髓经|Go技术论坛刘丹冰Aceld感谢以上文章作者,收获满满......
  • RTMP流媒体服务器LntonMedia(免费版)视频直播点播平台采用Golang指针问题导致平台重复推
    我们的团队在研发视频流媒体平台时,广泛应用了Go语言。之前我们也与大家交流过关于Go语言指针的问题和应用。如果你对视频流媒体平台编译中如何运用Go语言指针感兴趣,可以了解一下我们的讨论。在对LntonMedia的编译中,我们发现Golang指针问题会导致系统内的重复推流。Golang遍历切片代......
  • golang网络编程
    1简介Go语言的网络编程主要使用net包来实现。该包提供了一组基本的网络功能,包括TCP和UDP套接字、IP地址和端口号的处理、以及一些高级特性,如非阻塞I/O和HTTP客户端库。本文简单介绍一下如何使用net包进行TCP通信2TCP通信TCP服务端处理流程:监听端口接收......
  • golang 构造函数的应用
    在Go语言中,没有类似于传统面向对象编程语言中的构造函数的概念。然而,你可以使用初始化函数来达到类似的效果。在Go中,结构体(struct)是一种用于封装一组相关字段的数据类型。你可以为结构体定义一个初始化函数,该函数在创建结构体实例时自动调用,用于设置字段的初始值。这个初始化函数......
  • golang之协程+chan通道
     [管道]分为有缓冲和无缓冲两种无缓冲: 1)接受者与发送者必然存在于两个协程,否则会造成互相等待死锁的情况顺序执行多协程:varch1=make(chanint)varstopFlag=make(chanbool)//保证两个协程顺序执行gofunc(){fmt.Println("g1")......
  • golang 学习笔记
    1.函数调用时传递的参数为拷贝的副本,在函数内部改变参数的值不会影响原变量。但是golang中slice、map、channel、pointer、function是引用类型,赋值时拷贝的是指针值,对这些变量作出修改时会影响原变量的值。2.array(数组)与slice(切片)的区别1.array1. 长度......
  • Golang - 原生go-sql-driver:出现invalid connection报错
    在使用go-sql-driver/msqyl驱动过程中,偶现invalidconnection错误,字面上看就是无效连接的意思。开始以为是数据库压力问题或是网络不好,后来发现服务器和数据库是走内网的,网络出现问题几率非常小;只是在测试服务器上跑,没多少连接,不存在压力问题。golang数据库驱动维护一个连接池,如......
  • Golang 绘图技术(image/draw包介绍)
     image/draw包仅仅定义了一个操作:通过可选的蒙版图(maskimage),把一个原始图片绘制到目标图片上,这个操作是出奇的灵活,可以优雅和高效的执行很多常见的图像处理任务。1://DrawcallsDrawMaskwithanilmask.2:funcDraw(dstImage,rimage.Rectangle,srcimage.Image......
  • 使用golang解决LeetCode热题Hot100(1-10)
    使用golang解决LeetCode热题Hot1001.两数之和https://leetcode.cn/problems/two-sum/题目给定一个整数数组nums和一个整数目标值target,请你在该数组中找出和为目标值target的那两个整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个......
  • golang自定义 os.stderr 数据读取逻辑
    原始需求只是一个很简单的需求,使用golang的exec运行一个命令然后获取实时结果,命令是trivyimage--download-db-only正常的打印应该是2023-08-08T17:06:02.929+0800INFONeedtoupdateDB2023-08-08T17:06:02.929+0800INFODBRepository:ghcr.io/aquas......