diff --git a/.gitignore b/.gitignore index 66fd13c..1c2d52b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1 @@ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Dependency directories (remove the comment below to include it) -# vendor/ +.idea/* diff --git a/README.md b/README.md index 2c91684..6543de3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,31 @@ # gls goroutine local storage + +# demo +``` +package main + +import ( + "fmt" + "github.com/go-basic/gls" + "sync" +) + +func main() { + var wg sync.WaitGroup + for i := 0; i < 5; i++ { + wg.Add(1) + go func(idx int) { + defer wg.Done() + defer gls.Clean() + + defer func() { + fmt.Printf("%d: number = %d\n", idx, gls.Get("number")) + }() + gls.Set("number", idx+100) + }(i) + } + wg.Wait() +} + +``` diff --git a/examples/main.go b/examples/main.go new file mode 100644 index 0000000..180098c --- /dev/null +++ b/examples/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "fmt" + "github.com/go-basic/gls" + "sync" +) + +func main() { + var wg sync.WaitGroup + for i := 0; i < 5; i++ { + wg.Add(1) + go func(idx int) { + defer wg.Done() + defer gls.Clean() + + defer func() { + fmt.Printf("%d: number = %d\n", idx, gls.Get("number")) + }() + gls.Set("number", idx+100) + }(i) + } + wg.Wait() +} diff --git a/gls.go b/gls.go new file mode 100644 index 0000000..b57a3ab --- /dev/null +++ b/gls.go @@ -0,0 +1,55 @@ +package gls + +import ( + "fmt" + "runtime" + "strconv" + "strings" + "sync" +) + +var gls struct { + m map[int64]map[interface{}]interface{} + sync.RWMutex +} + +func init() { + gls.m = make(map[int64]map[interface{}]interface{}) +} + +func GetGoId() int64 { + var ( + buf [64]byte + n = runtime.Stack(buf[:], false) + stk = strings.TrimPrefix(string(buf[:n]), "goroutine ") + ) + idField := strings.Fields(stk)[0] + id, err := strconv.Atoi(idField) + if err != nil { + panic(fmt.Errorf("can not get goroutine id: %v", err)) + } + return int64(id) +} + +func Get(key interface{}) interface{} { + gls.RLock() + defer gls.RUnlock() + goId := GetGoId() + return gls.m[goId][key] +} + +func Set(key interface{}, v interface{}) { + gls.Lock() + defer gls.Unlock() + goId := GetGoId() + if _, ok := gls.m[goId][key]; !ok { + gls.m[goId] = make(map[interface{}]interface{}) + } + gls.m[goId][key] = v +} + +func Clean() { + gls.Lock() + defer gls.Unlock() + delete(gls.m, GetGoId()) +} diff --git a/gls_test.go b/gls_test.go new file mode 100644 index 0000000..844be66 --- /dev/null +++ b/gls_test.go @@ -0,0 +1,51 @@ +package gls + +import ( + "fmt" + "strconv" + "testing" +) + +var key = "test" + +func GO(fn func()) { + go fn() +} + +func TestGls(t *testing.T) { + GO(func() { + defer Clean() + Set(key, "aaa") + testPrint() + }) + GO(func() { + defer Clean() + Set(key, "bbb") + testPrint() + }) + GO(func() { + defer Clean() + Set(key, "ccc") + testPrint() + }) +} + + +func BenchmarkGls(b *testing.B) { + for i := 0; i < b.N; i++ { + GO(func() { + defer Clean() + Set(key, "ccc"+strconv.FormatInt(GetGoId(), 10)) + testGet() + }) + } +} + +func testPrint() { + fmt.Println(Get(key), GetGoId()) +} + +func testGet() { + Get(key) +} + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..27b605b --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/go-basic/gls + +go 1.15