修改golang源代码实现无竞争版ThreadLocal

栏目: Java · 发布时间: 7年前

内容简介:书接上文先看看java里面怎么实现的可以看到每个线程实例都引用了一个map,map的key是ThreadLocal对象,value是实际存储的数据。下面我们也按照这个思路来实现。

开篇

书接上文 修改golang源代码获取goroutine id实现ThreadLocal 。上文实现的版本由于map是多个goroutine共享的,存在竞争,影响了性能,实现思路类似 java 初期的ThreadLocal,今天我们借鉴现代版java的ThreadLocal来实现。

思路

先看看java里面怎么实现的

修改golang源代码实现无竞争版ThreadLocal

image.png

可以看到每个线程实例都引用了一个map,map的key是ThreadLocal对象,value是实际存储的数据。下面我们也按照这个思路来实现。

实现

修改g结构

修改 $GOROOT/src/runtime/runtime2.go 文件,为g结构体添加 localMap *goroutineLocalMap 字段

type g struct {
    // Stack parameters.
    // stack describes the actual stack memory: [stack.lo, stack.hi).
    // stackguard0 is the stack pointer compared in the Go stack growth prologue.
    // It is stack.lo+StackGuard normally, but can be StackPreempt to trigger a preemption.
    // stackguard1 is the stack pointer compared in the C stack growth prologue.
    // It is stack.lo+StackGuard on g0 and gsignal stacks.
    // It is ~0 on other goroutine stacks, to trigger a call to morestackc (and crash).
    stack       stack   // offset known to runtime/cgo
    stackguard0 uintptr // offset known to liblink
    stackguard1 uintptr // offset known to liblink

    _panic         *_panic // innermost panic - offset known to liblink
    _defer         *_defer // innermost defer
    m              *m      // current m; offset known to arm liblink
    sched          gobuf
    syscallsp      uintptr        // if status==Gsyscall, syscallsp = sched.sp to use during gc
    syscallpc      uintptr        // if status==Gsyscall, syscallpc = sched.pc to use during gc
    stktopsp       uintptr        // expected sp at top of stack, to check in traceback
    param          unsafe.Pointer // passed parameter on wakeup
    atomicstatus   uint32
    stackLock      uint32 // sigprof/scang lock; TODO: fold in to atomicstatus
    goid           int64
    schedlink      guintptr
    waitsince      int64      // approx time when the g become blocked
    waitreason     waitReason // if status==Gwaiting
    preempt        bool       // preemption signal, duplicates stackguard0 = stackpreempt
    paniconfault   bool       // panic (instead of crash) on unexpected fault address
    preemptscan    bool       // preempted g does scan for gc
    gcscandone     bool       // g has scanned stack; protected by _Gscan bit in status
    gcscanvalid    bool       // false at start of gc cycle, true if G has not run since last scan; TODO: remove?
    throwsplit     bool       // must not split stack
    raceignore     int8       // ignore race detection events
    sysblocktraced bool       // StartTrace has emitted EvGoInSyscall about this goroutine
    sysexitticks   int64      // cputicks when syscall has returned (for tracing)
    traceseq       uint64     // trace event sequencer
    tracelastp     puintptr   // last P emitted an event for this goroutine
    lockedm        muintptr
    sig            uint32
    writebuf       []byte
    sigcode0       uintptr
    sigcode1       uintptr
    sigpc          uintptr
    gopc           uintptr         // pc of go statement that created this goroutine
    ancestors      *[]ancestorInfo // ancestor information goroutine(s) that created this goroutine (only used if debug.tracebackancestors)
    startpc        uintptr         // pc of goroutine function
    racectx        uintptr
    waiting        *sudog         // sudog structures this g is waiting on (that have a valid elem ptr); in lock order
    cgoCtxt        []uintptr      // cgo traceback context
    labels         unsafe.Pointer // profiler labels
    timer          *timer         // cached timer for time.Sleep
    selectDone     uint32         // are we participating in a select and did someone win the race?
    // Per-G GC state

    // gcAssistBytes is this G's GC assist credit in terms of
    // bytes allocated. If this is positive, then the G has credit
    // to allocate gcAssistBytes bytes without assisting. If this
    // is negative, then the G must correct this by performing
    // scan work. We track this in bytes to make it fast to update
    // and check for debt in the malloc hot path. The assist ratio
    // determines how this corresponds to scan work debt.
    gcAssistBytes int64
    localMap *goroutineLocalMap //这是我们添加的
}

注意不要放在第一个字段,否则编译会出现 fatal: morestack on g0

实现goroutineLocal

在 $GOROOT/src/runtime/ 目录下创建 go 原文件 goroutine_local.go

package runtime


type goroutineLocalMap struct {
    m map[*goroutineLocal]interface{}
}

type goroutineLocal struct {
    initfun func() interface{}
}

func NewGoroutineLocal(initfun func() interface{}) *goroutineLocal {
    return &goroutineLocal{initfun}
}

func (gl *goroutineLocal)Get() interface{} {
    if getg().localMap == nil {
        getg().localMap = &goroutineLocalMap{make(map[*goroutineLocal]interface{})}
    }
    v, ok := getg().localMap.m[gl]
    if !ok && gl.initfun != nil{
        v = gl.initfun()
    }
    return v
}

func (gl *goroutineLocal)Set(v interface{}) {
    if getg().localMap == nil {
        getg().localMap = &goroutineLocalMap{make(map[*goroutineLocal]interface{})}
    }
    getg().localMap.m[gl] = v
}

func (gl *goroutineLocal)Remove() {
    if getg().localMap != nil {
        delete(getg().localMap.m, gl)
    }
}

重新编译

cd ~/go/src
GOROOT_BOOTSTRAP='/Users/qiuxudong/go1.9' ./all.bash

写个mian函数测试一下

package main

import (
    "fmt"
    "time"
    "runtime"
)

var gl = runtime.NewGoroutineLocal(func() interface{} {
    return "default"
})

func main() {
    gl.Set("test0")
    fmt.Println(runtime.GetGoroutineId(), gl.Get())


    go func() {
        gl.Set("test1")
        fmt.Println(runtime.GetGoroutineId(), gl.Get())
        gl.Remove()
        fmt.Println(runtime.GetGoroutineId(), gl.Get())
    }()


    time.Sleep(2 * time.Second)
}

可以看到

1 test0
18 test1
18 default

同样的,这个版本也可能会内存泄露,建议主动调用Remove清除数据。但是如果goroutine销毁了,对应的数据不再被引用,是可以被GC清理的,泄露的概率降低很多。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

Pattern Recognition and Machine Learning

Pattern Recognition and Machine Learning

Christopher Bishop / Springer / 2007-10-1 / USD 94.95

The dramatic growth in practical applications for machine learning over the last ten years has been accompanied by many important developments in the underlying algorithms and techniques. For example,......一起来看看 《Pattern Recognition and Machine Learning》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具