站长网 语言 Semaphore 数据结构分解详解

Semaphore 数据结构分解详解

副标题#e# //Go语言中暴露的semaphore实现 //具体的用法是提供sleep和wakeup原语 //以使其能够在其它同步原语中的竞争情况下使用 //因此这里的semaphore和Linux中的futex目标是一致的 //只不过语义上更简单一些 // //也就是说,不要认为这些是信号量 //把这

副标题#e#

// Go 语言中暴露的 semaphore 实现 

// 具体的用法是提供 sleep 和 wakeup 原语 

// 以使其能够在其它同步原语中的竞争情况下使用 

// 因此这里的 semaphore 和 Linux 中的 futex 目标是一致的 

// 只不过语义上更简单一些 

// 

// 也就是说,不要认为这些是信号量 

// 把这里的东西看作 sleep 和 wakeup 实现的一种方式 

// 每一个 sleep 都会和一个 wakeup 配对 

// 即使在发生 race 时,wakeup 在 sleep 之前时也是如此 

// 

// See Mullender and Cox, “Semaphores in Plan 9,'' 

//  

 

// 为 sync.Mutex 准备的异步信号量 

 

// semaRoot 持有一棵 地址各不相同的 sudog(s.elem) 的平衡树 

// 每一个 sudog 都反过来指向(通过 s.waitlink)一个在同一个地址上等待的其它 sudog 们 

// 同一地址的 sudog 的内部列表上的操作时间复杂度都是 O(1)。顶层 semaRoot 列表的扫描 

// 的时间复杂度是 O(log n),n 是被哈希到同一个 semaRoot 的不同地址的总数,每一个地址上都会有一些 goroutine 被阻塞。 

// 访问 golang.org/issue/17953 来查看一个在引入二级列表之前性能较差的程序样例,test/locklinear.go 

// 中有一个复现这个样例的测试 

type semaRoot struct { 

    lock  mutex 

    treap *sudog // root of balanced tree of unique waiters. 

    nwait uint32 // Number of waiters. Read w/o the lock. 

 

// Prime to not correlate with any user patterns. 

const semTabSize = 251 

 

var semtable [semTabSize]struct { 

    root semaRoot 

    pad  [sys.CacheLineSize – unsafe.Sizeof(semaRoot{})]byte 

 

func semroot(addr *uint32) *semaRoot { 

    return &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root 

┌─────┬─────┬─────┬─────┬─────┬────────────────────────┬─────┐                  

│  0  │  1  │  2  │  3  │  4  │         …..          │ 250 │                  

└─────┴─────┴─────┴─────┴─────┴────────────────────────┴─────┘                  

   │                                                      │                     

   │                                                      │                     

#p#副标题#e#

   └──┐                                                   └─┐                   

#p#副标题#e##p#分页标题#e#

      │                                                     │                   

      │                                                     │                   

      ▼                                                     ▼                   

 ┌─────────┐                                           ┌─────────┐              

 │ struct  │                                           │ struct  │              

 ├─────────┴─────────┐                                 ├─────────┴─────────┐    

 │   root semaRoot   │──┐                              │   root semaRoot   │──┐ 

 ├───────────────────┤  │                              ├───────────────────┤  │ 

 │        pad        │  │                              │        pad        │  │ 

 └───────────────────┘  │                              └───────────────────┘  │ 

                        │                                                     │ 

#p#副标题#e#

       ┌────────────────┘                                    ┌────────────────┘ 

       │                                                     │                  

#p#副标题#e##p#分页标题#e#

       │                                                     │                  

       ▼                                                     ▼                  

 ┌──────────┐                                          ┌──────────┐             

 │ semaRoot │                                          │ semaRoot │             

 ├──────────┴────────┐                                 ├──────────┴────────┐    

 │    lock mutex     │                                 │    lock mutex     │    

 ├───────────────────┤                                 ├───────────────────┤    

 │   treap *sudog    │                                 │   treap *sudog    │    

 ├───────────────────┤                                 ├───────────────────┤    

 │   nwait uint32    │                                 │   nwait uint32    │    

#p#副标题#e#

 └───────────────────┘                                 └───────────────────┘ 

treap 结构:

                                 ┌──────────┐                                     

                            ┌─┬─▶│  sudog   │                                     

#p#副标题#e##p#分页标题#e#

                            │ │  ├──────────┴────────────┐                        

      ┌─────────────────────┼─┼──│      prev *sudog      │                        

      │                     │ │  ├───────────────────────┤                        

      │                     │ │  │      next *sudog      │────┐                   

      │                     │ │  ├───────────────────────┤    │                   

      │                     │ │  │     parent *sudog     │    │                   

      │                     │ │  ├───────────────────────┤    │                   

      │                     │ │  │  elem unsafe.Pointer  │    │                   

      │                     │ │  ├───────────────────────┤    │                   

      │                     │ │  │     ticket uint32     │    │                   

#p#副标题#e#

      │                     │ │  └───────────────────────┘    │                   

      │                     │ │                               │                   

      │                     │ │                               │                   

#p#副标题#e##p#分页标题#e#

      │                     │ │                               │                   

      │                     │ │                               │                   

      │                     │ │                               │                   

      │                     │ │                               │                   

      ▼                     │ │                               ▼                   

┌──────────┐                │ │                         ┌──────────┐              

│  sudog   │                │ │                         │  sudog   │              

#p#副标题#e#

├──────────┴────────────┐   │ │                         ├──────────┴────────────┐ 

│      prev *sudog      │   │ │                         │      prev *sudog      │ 

├───────────────────────┤   │ │                         ├───────────────────────┤ 

│      next *sudog      │   │ │                         │      next *sudog      │ 

├───────────────────────┤   │ │                         ├───────────────────────┤ 

#p#副标题#e##p#分页标题#e#

│     parent *sudog     │───┘ └─────────────────────────│     parent *sudog     │ 

├───────────────────────┤                               ├───────────────────────┤ 

│  elem unsafe.Pointer  │                               │  elem unsafe.Pointer  │ 

├───────────────────────┤                               ├───────────────────────┤ 

│     ticket uint32     │                               │     ticket uint32     │ 

└───────────────────────┘                               └───────────────────────┘ 

在这个 treap 结构里,从 elem 的视角(其实就是 lock 的 addr)来看,这个结构是个二叉搜索树。从 ticket 的角度来看,整个结构就是一个小顶堆。

所以才叫树堆(treap)。

相同 addr,即对同一个 mutex 上锁的 g,会阻塞在同一个地址上。这些阻塞在同一个地址上的 goroutine 会被打包成 sudog,组成一个链表。用 sudog 的 waitlink 相连:

┌──────────┐                         ┌──────────┐                          ┌──────────┐              

│  sudog   │                  ┌─────▶│  sudog   │                   ┌─────▶│  sudog   │              

├──────────┴────────────┐     │      ├──────────┴────────────┐      │      ├──────────┴────────────┐ 

#p#副标题#e#

│    waitlink *sudog    │─────┘      │    waitlink *sudog    │──────┘      │    waitlink *sudog    │ 

├───────────────────────┤            ├───────────────────────┤             ├───────────────────────┤ 

│    waittail *sudog    │            │    waittail *sudog    │             │    waittail *sudog    │ 

└───────────────────────┘            └───────────────────────┘             └───────────────────────┘ 

中间的元素的 waittail 都会指向最后一个元素:

┌──────────┐                                                                                            

#p#副标题#e##p#分页标题#e#

│  sudog   │                                                                                            

├──────────┴────────────┐                                                                               

│    waitlink *sudog    │                                                                               

├───────────────────────┤                                                                               

│    waittail *sudog    │───────────────────────────────────────────────────────────┐                   

└───────────────────────┘                                                           │                   

#p#副标题#e#

                                  ┌──────────┐                                      │                   

                                  │  sudog   │                                      │                   

                                  ├──────────┴────────────┐                         │                   

#p#副标题#e##p#分页标题#e#

                                  │    waitlink *sudog    │                         │                   

                                  ├───────────────────────┤                         │                   

                                  │    waittail *sudog    │─────────────────────────┤                   

                                  └───────────────────────┘                         ▼                   

                                                                              ┌──────────┐              

                                                                              │  sudog   │              

#p#副标题#e#

                                                                              ├──────────┴────────────┐ 

                                                                              │    waitlink *sudog    │ 

                                                                              ├───────────────────────┤ 

#p#分页标题#e#

                                                                              │    waittail *sudog    │ 

#p#副标题#e#

                                                                              └───────────────────────┘ 

对外封装

在 sema.go 里实现的内容,用 go:linkname 导出给 sync、poll 库来使用,也是在链接期做了些手脚:

//go:linkname sync_runtime_Semacquire sync.runtime_Semacquire 

func sync_runtime_Semacquire(addr *uint32) { 

    semacquire1(addr, false, semaBlockProfile) 

 

//go:linkname poll_runtime_Semacquire internal/poll.runtime_Semacquire 

func poll_runtime_Semacquire(addr *uint32) { 

    semacquire1(addr, false, semaBlockProfile) 

 

//go:linkname sync_runtime_Semrelease sync.runtime_Semrelease 

func sync_runtime_Semrelease(addr *uint32, handoff bool) { 

    semrelease1(addr, handoff) 

 

//go:linkname sync_runtime_SemacquireMutex sync.runtime_SemacquireMutex 

func sync_runtime_SemacquireMutex(addr *uint32, lifo bool) { 

    semacquire1(addr, lifo, semaBlockProfile|semaMutexProfile) 

 

//go:linkname poll_runtime_Semrelease internal/poll.runtime_Semrelease 

func poll_runtime_Semrelease(addr *uint32) { 

    semrelease(addr) 

本文来自网络,不代表站长网立场,转载请注明出处:https://www.tzzz.com.cn/html/biancheng/yuyan/2021/0525/6412.html

作者: dawei

【声明】:站长网内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。
联系我们

联系我们

0577-28828765

在线咨询: QQ交谈

邮箱: xwei067@foxmail.com

工作时间:周一至周五,9:00-17:30,节假日休息

返回顶部