├── 5.channel ├── .dummy ├── closed │ └── main.go ├── order │ └── main.go ├── rw │ └── main.go ├── or_done_channel │ └── main.go ├── trylock │ └── main.go ├── trylock_channel2 │ └── main.go ├── trylock_channel │ └── main.go ├── or_channel_go │ └── main.go ├── trylock_timeout │ └── main.go ├── or_channel_reflect │ └── main.go ├── or_channel_rec │ └── main.go ├── mapreduce │ └── main.go ├── or_channel │ └── main.go ├── flat │ └── main.go ├── perf │ └── chan_test.go ├── fanout │ └── main.go ├── fanout2 │ └── main.go ├── fanin │ └── main.go ├── eapache │ └── main.go └── stream │ └── main.go ├── 10.ratelimit ├── README.md ├── go.mod ├── x │ └── main.go ├── juju │ └── main.go ├── uber │ └── main.go └── go.sum ├── 3.atomic ├── asm │ ├── main.o │ ├── buildpkg.sh │ ├── main.go │ ├── asm.sh │ └── asm.md ├── uber │ └── main.go ├── value │ └── main.go ├── pointer │ └── main.go └── lock-free │ └── value_test.go ├── 12.scheduler ├── schedule │ ├── schedule │ └── schedule.go └── trace │ └── trace.go ├── 1.basic ├── once │ ├── singleton │ │ └── io.go │ ├── wrong │ │ └── once.go │ ├── deadlock │ │ └── once.go │ ├── error2 │ │ └── once.go │ ├── error │ │ └── once.go │ ├── recover │ │ └── main.go │ ├── reset │ │ └── main.go │ └── helper │ │ └── main.go ├── mutex │ ├── err_staticcheck │ │ └── main.go │ ├── err_more_unlock │ │ └── main.go │ ├── no_err │ │ └── main.go │ ├── err_reentrant_lock │ │ └── main.go │ ├── timeout │ │ └── timeout.go │ ├── syncmap │ │ └── main.go │ ├── value │ │ └── main.go │ └── trylock │ │ └── main.go ├── map │ ├── concurrent │ │ └── map.go │ ├── mapkey │ │ └── map.go │ └── rwmap │ │ └── rwmap.go ├── time │ ├── afterFunc │ │ └── after.go │ ├── ticker │ │ └── ticker.go │ ├── after │ │ └── after.go │ ├── rand_ticker │ │ └── rand_ticker.go │ └── timer │ │ └── reset.go ├── wg │ ├── wg5 │ │ └── waitgroup.go │ ├── wg6 │ │ └── waitgroup.go │ ├── wg3 │ │ └── waitgroup.go │ ├── wg4 │ │ └── waitgroup.go │ ├── wg7 │ │ └── waitgroup.go │ ├── wg2 │ │ └── waitgroup.go │ ├── wg9 │ │ └── waitgroup.go │ ├── wg8 │ │ └── waitgroup.go │ ├── wg1 │ │ └── waitgroup.go │ └── wg10 │ │ └── main.go ├── context │ ├── chain │ │ └── main.go │ ├── deadline │ │ └── main.go │ ├── cancel2 │ │ └── main.go │ └── cancel │ │ └── main.go ├── cond2 │ └── main.go ├── rwmutex │ ├── recursive │ │ └── main.go │ ├── readwrite │ │ └── main.go │ └── hack │ │ └── main.go └── cond │ └── main.go ├── .gitignore ├── 9.group ├── slice │ └── main.go ├── syncs │ └── group.go ├── hunch │ └── all │ │ └── all.go ├── gogroup │ └── main.go ├── syncs2 │ └── group.go ├── rungroup │ └── main.go ├── errgroup │ └── errg.go ├── syncx │ └── main.go ├── facebook │ └── errg.go ├── errgroup2 │ └── errg.go ├── bilibili │ └── main.go ├── errgroup3 │ └── errg.go ├── schedgroup │ └── main.go └── errgroup4 │ └── md5all.go ├── 11.classical_problems ├── README.md ├── barbershop_problem0 │ └── main.go ├── barbershop_problem1 │ └── main.go ├── barbershop_problem2 │ └── main.go ├── h2o_problem2 │ └── main.go ├── dining_philosophers_problem0 │ └── main.go ├── h2o_problem1_2 │ └── main.go ├── h2o_problem1 │ └── main.go ├── hilzers_barbershop_problem │ └── main.go ├── dining_philosophers_problem4 │ └── main.go ├── h2o_problem0 │ └── main.go ├── dining_philosophers_problem1 │ └── main.go ├── h2o2_problem3 │ └── main.go ├── dining_philosophers_problem2 │ └── main.go └── dining_philosophers_problem3 │ └── main.go ├── 2.ext ├── reentrant_lock │ ├── goid2 │ │ └── main.go │ ├── goid │ │ └── main.go │ ├── token_recursive_mutex.go │ ├── stacks │ │ └── main.go │ └── recursive_mutex.go ├── future │ └── main.go ├── cyclicbarrier │ └── cyclicbarrier.go ├── spinlock │ └── main.go ├── optimistic_lock │ └── main.go └── semaphore │ └── sema.go ├── 8.pool ├── bytebufferpool │ └── main.go ├── connpool │ └── main.go ├── workerpool │ └── main.go ├── goworkers │ └── main.go └── grpool │ └── main.go ├── 7.orchestration ├── water3 │ ├── water.go │ └── water_test.go ├── water │ ├── water.go │ └── water_test.go ├── water2 │ ├── water.go │ └── water_test.go ├── fizzbuzz3 │ └── fizzbuzz.go ├── fizzbuzz2 │ └── fizzbuzz.go └── fizzbuzz1 │ └── fizzbuzz.go ├── 6.happenbefore └── double-checking │ └── main.go ├── 4.distributed ├── locker │ └── locker.go ├── mutex │ └── mutex.go ├── queue │ └── queue.go ├── priority_queue │ └── priority_queue.go ├── stm │ └── stm.go ├── rwmutex │ └── rwmutex.go ├── barrier │ └── barrier.go └── lead_election │ └── leader.go ├── README.md └── go.mod /5.channel/.dummy: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /10.ratelimit/README.md: -------------------------------------------------------------------------------- 1 | https://en.wikipedia.org/wiki/Token_bucket -------------------------------------------------------------------------------- /3.atomic/asm/main.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallnest/dive-to-gosync-workshop/HEAD/3.atomic/asm/main.o -------------------------------------------------------------------------------- /12.scheduler/schedule/schedule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallnest/dive-to-gosync-workshop/HEAD/12.scheduler/schedule/schedule -------------------------------------------------------------------------------- /1.basic/once/singleton/io.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | ) 7 | 8 | func main() { 9 | io.EOF = errors.New("我们自己定义的EOF") 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | -------------------------------------------------------------------------------- /9.group/slice/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | var s []int 7 | for i := 0; i < 100000; i++ { 8 | go func(n int) { 9 | s = append(s, n) 10 | }(i) 11 | } 12 | fmt.Printf("%d\n", s[0]) 13 | } 14 | -------------------------------------------------------------------------------- /1.basic/once/wrong/once.go: -------------------------------------------------------------------------------- 1 | package wrong 2 | 3 | import "sync/atomic" 4 | 5 | type Once struct { 6 | done uint32 7 | } 8 | 9 | func (o *Once) Do(f func()) { 10 | if !atomic.CompareAndSwapUint32(&o.done, 0, 1) { 11 | return 12 | } 13 | f() 14 | } 15 | -------------------------------------------------------------------------------- /1.basic/once/deadlock/once.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func main() { 9 | var once sync.Once 10 | 11 | once.Do(func() { 12 | once.Do(func() { 13 | fmt.Println("初始化") 14 | }) 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /1.basic/mutex/err_staticcheck/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | var mu sync.Mutex 9 | 10 | func main() { 11 | go lock() 12 | time.Sleep(1e9) 13 | } 14 | 15 | func lock() { 16 | mu.Lock() 17 | defer mu.Lock() 18 | } 19 | -------------------------------------------------------------------------------- /11.classical_problems/README.md: -------------------------------------------------------------------------------- 1 | # 哲学家就餐问题 2 | 3 | ## 解法一 4 | 5 | 限制最大同时就餐人数为4人。根据抽屉原理,必有一人可以获得两根筷子。 6 | 7 | ## 解法二 8 | 9 | 奇数哲学家先拿左边的筷子,偶数哲学家先拿右边的筷子。 10 | 11 | ## 解法三 12 | 13 | 就餐前,先取用编号较低的餐叉,再取用编号较高的餐叉 14 | 就餐毕,先放下编号较高的餐叉,再放下编号较低的餐叉 15 | 16 | ## 解法四 17 | 18 | 引入串行机制,那筷子的时候排队。(服务生) -------------------------------------------------------------------------------- /1.basic/map/concurrent/map.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | m := make(map[int]int, 10) 5 | 6 | go func() { 7 | for { 8 | m[1] = 1 9 | } 10 | }() 11 | 12 | go func() { 13 | for { 14 | _ = m[2] 15 | } 16 | }() 17 | 18 | select {} 19 | } 20 | -------------------------------------------------------------------------------- /1.basic/mutex/err_more_unlock/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | var mu sync.Mutex 10 | go func() { 11 | mu.Lock() 12 | time.Sleep(5 * time.Second) 13 | mu.Unlock() 14 | }() 15 | 16 | time.Sleep(time.Second) 17 | mu.Unlock() 18 | 19 | select {} 20 | } 21 | -------------------------------------------------------------------------------- /1.basic/map/mapkey/map.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Counter struct { 8 | Website string 9 | Start time.Time 10 | PageCounters map[string]int 11 | } 12 | 13 | func main() { 14 | var c Counter 15 | c.Website = "baidu.com" 16 | 17 | c.PageCounters["/"]++ 18 | } 19 | -------------------------------------------------------------------------------- /1.basic/time/afterFunc/after.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | timer := time.AfterFunc(time.Second, func() { 11 | fmt.Println("fired") 12 | }) 13 | 14 | t := <-timer.C // nil 15 | log.Printf("fired at %s", t.String()) // 海枯石烂你也等不来 16 | } 17 | -------------------------------------------------------------------------------- /1.basic/wg/wg5/waitgroup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | func main() { 8 | var wg sync.WaitGroup 9 | wg.Add(1) 10 | 11 | go func() { 12 | for { 13 | wg.Done() 14 | wg.Add(1) 15 | } 16 | }() 17 | 18 | go func() { 19 | for { 20 | wg.Wait() 21 | } 22 | }() 23 | 24 | select {} 25 | } 26 | -------------------------------------------------------------------------------- /3.atomic/uber/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "go.uber.org/atomic" 7 | ) 8 | 9 | func main() { 10 | var atom atomic.Uint32 11 | 12 | // The wrapper ensures that all operations are atomic. 13 | atom.Store(42) 14 | fmt.Println(atom.Inc()) 15 | fmt.Println(atom.CAS(43, 0)) 16 | fmt.Println(atom.Load()) 17 | } 18 | -------------------------------------------------------------------------------- /1.basic/wg/wg6/waitgroup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | // https://groups.google.com/forum/#!topic/golang-nuts/bv_Qac26fDc 9 | func main() { 10 | var wg sync.WaitGroup 11 | wg.Add(1) 12 | go func() { 13 | time.Sleep(time.Millisecond) 14 | wg.Done() 15 | wg.Add(1) 16 | }() 17 | wg.Wait() 18 | } 19 | -------------------------------------------------------------------------------- /12.scheduler/schedule/schedule.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | func main() { 6 | var wg sync.WaitGroup 7 | 8 | for i := 0; i < 2000; i++ { 9 | wg.Add(1) 10 | go func() { 11 | a := 0 12 | 13 | for i := 0; i < 1e8; i++ { 14 | a += 1 15 | } 16 | 17 | wg.Done() 18 | }() 19 | } 20 | 21 | wg.Wait() 22 | } 23 | -------------------------------------------------------------------------------- /2.ext/reentrant_lock/goid2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "time" 6 | 7 | "github.com/petermattis/goid" 8 | ) 9 | 10 | func main() { 11 | for i := 0; i < 10; i++ { 12 | go func() { 13 | for j := 0; j < 1000000; j++ { 14 | log.Printf("[#%d] %d", goid.Get(), j) 15 | time.Sleep(10e9) 16 | } 17 | }() 18 | } 19 | select {} 20 | } 21 | -------------------------------------------------------------------------------- /1.basic/mutex/no_err/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | var m sync.Mutex 11 | fmt.Print("A, ") 12 | m.Lock() 13 | 14 | go func() { 15 | time.Sleep(200 * time.Millisecond) 16 | m.Unlock() 17 | }() 18 | 19 | // 等另一个goroutine释放锁之后,main goroutine就可以获取到锁 20 | m.Lock() 21 | fmt.Print("B ") 22 | } 23 | -------------------------------------------------------------------------------- /3.atomic/asm/buildpkg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -z "$1" ]; then 3 | echo 'GOOS is not specified' 1>&2 4 | exit 2 5 | else 6 | export GOOS=$1 7 | if [ "$GOOS" = "windows" ]; then 8 | export CGO_ENABLED=0 9 | fi 10 | fi 11 | shift 12 | if [ -n "$1" ]; then 13 | export GOARCH=$1 14 | fi 15 | cd $GOROOT/src 16 | go tool dist install -v pkg/runtime 17 | go install -v -a std 18 | -------------------------------------------------------------------------------- /8.pool/bytebufferpool/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/valyala/bytebufferpool" 7 | ) 8 | 9 | func main() { 10 | bb := bytebufferpool.Get() 11 | 12 | bb.WriteString("first line\n") 13 | bb.Write([]byte("second line\n")) 14 | bb.B = append(bb.B, "third line\n"...) 15 | 16 | fmt.Printf("bytebuffer contents=%q", bb.B) 17 | 18 | bytebufferpool.Put(bb) 19 | } 20 | -------------------------------------------------------------------------------- /5.channel/closed/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | receive() 10 | } 11 | 12 | func receive() { 13 | ch := make(chan int, 100) 14 | for i := 0; i < 10; i++ { 15 | ch <- i 16 | } 17 | // ch <- 0 18 | close(ch) // !!!!!! 19 | 20 | for { 21 | i, ok := <-ch 22 | fmt.Println(i, ok) 23 | time.Sleep(time.Second) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /8.pool/connpool/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "github.com/fatih/pool" 8 | ) 9 | 10 | func main() { 11 | factory := func() (net.Conn, error) { return net.Dial("tcp", "baidu.com:80") } 12 | 13 | p, err := pool.NewChannelPool(5, 30, factory) 14 | if err != nil { 15 | panic(err) 16 | } 17 | 18 | conn, err := p.Get() 19 | conn.Close() 20 | 21 | fmt.Println(p.Len()) 22 | } 23 | -------------------------------------------------------------------------------- /1.basic/context/chain/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | func main() { 9 | ctx := context.Background() 10 | ctx = context.TODO() 11 | ctx = context.WithValue(ctx, "key1", "0001") 12 | ctx = context.WithValue(ctx, "key2", "0001") 13 | ctx = context.WithValue(ctx, "key3", "0001") 14 | ctx = context.WithValue(ctx, "key4", "0004") 15 | 16 | fmt.Println(ctx.Value("key1")) 17 | } 18 | -------------------------------------------------------------------------------- /3.atomic/value/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync/atomic" 6 | ) 7 | 8 | type T struct{ a int } 9 | 10 | func main() { 11 | var tv atomic.Value 12 | 13 | var ta, tb T 14 | // store 15 | tv.Store(ta) 16 | 17 | // load 18 | tv1 := tv.Load().(T) 19 | fmt.Println(tv1 == ta) // true 20 | 21 | // store another 22 | tv.Store(tb) 23 | tv2 := tv.Load().(T) 24 | fmt.Println(tv2 == tb) // true 25 | } 26 | -------------------------------------------------------------------------------- /3.atomic/asm/main.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "sync/atomic" 5 | ) 6 | 7 | func add(i *int64) { 8 | atomic.AddInt64(i, 100) // 9 | } 10 | 11 | func cas(i *int64) { 12 | atomic.CompareAndSwapInt64(i, 0, 100) 13 | } 14 | 15 | func load(i *int64) { 16 | atomic.LoadInt64(i) 17 | } 18 | 19 | func store(i *int64) { 20 | atomic.StoreInt64(i, 100) 21 | } 22 | 23 | func swap(i *int64) { 24 | atomic.SwapInt64(i, 100) 25 | } 26 | -------------------------------------------------------------------------------- /1.basic/once/error2/once.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "os" 7 | "sync" 8 | ) 9 | 10 | func main() { 11 | var once sync.Once 12 | var googleConn net.Conn 13 | 14 | once.Do(func() { 15 | googleConn, _ = net.Dial("tcp", "google.com:80") 16 | }) 17 | 18 | googleConn.Write([]byte("GET / HTTP/1.1\r\nHost: google.com\r\n Accept: */*\r\n\r\n")) 19 | io.Copy(os.Stdout, googleConn) 20 | } 21 | -------------------------------------------------------------------------------- /1.basic/wg/wg3/waitgroup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | var count int64 12 | var wg sync.WaitGroup 13 | wg.Add(10) 14 | for i := 0; i < 10; i++ { 15 | go func() { 16 | atomic.AddInt64(&count, 1) 17 | time.Sleep(2 * time.Second) 18 | wg.Done() 19 | }() 20 | } 21 | wg.Done() 22 | wg.Wait() 23 | fmt.Println(atomic.LoadInt64(&count)) 24 | } 25 | -------------------------------------------------------------------------------- /5.channel/order/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | ch := make(chan int, 10) 10 | var i = 0 11 | 12 | for ; i < 10; i++ { 13 | ch <- i 14 | } 15 | 16 | go func() { 17 | for { 18 | i++ 19 | ch <- i 20 | } 21 | }() 22 | 23 | for j := 0; j < 50; j++ { 24 | time.Sleep(time.Second) 25 | k := <-ch // 是优先读取缓存的数据,还是直接读取阻塞住的gorontine发送的数据 26 | fmt.Println(k) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /1.basic/mutex/err_reentrant_lock/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func M1(l sync.Locker) { 9 | fmt.Println("in M1") 10 | l.Lock() 11 | M2(l) 12 | l.Unlock() 13 | } 14 | 15 | func M2(l sync.Locker) { 16 | fmt.Println("in M2") 17 | M3(l) 18 | } 19 | 20 | func M3(l sync.Locker) { 21 | l.Lock() 22 | fmt.Println("in M3") 23 | l.Unlock() 24 | } 25 | func main() { 26 | l := &sync.Mutex{} 27 | M1(l) 28 | } 29 | -------------------------------------------------------------------------------- /1.basic/wg/wg4/waitgroup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | var count int64 12 | var wg sync.WaitGroup 13 | wg.Add(10) 14 | for i := 0; i < 10; i++ { 15 | go func() { 16 | atomic.AddInt64(&count, 1) 17 | time.Sleep(2 * time.Second) 18 | wg.Done() 19 | }() 20 | } 21 | wg.Wait() 22 | wg.Wait() 23 | fmt.Println(atomic.LoadInt64(&count)) 24 | 25 | } 26 | -------------------------------------------------------------------------------- /1.basic/wg/wg7/waitgroup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // https://groups.google.com/forum/#!topic/golang-nuts/bv_Qac26fDc 8 | func main() { 9 | var wg sync.WaitGroup 10 | for i := 0; i < 100; i++ { 11 | go func() { 12 | for { 13 | wg.Add(1) 14 | wg.Done() 15 | } 16 | }() 17 | } 18 | for i := 0; i < 100; i++ { 19 | go func() { 20 | for { 21 | wg.Wait() 22 | } 23 | }() 24 | } 25 | 26 | select {} 27 | } 28 | -------------------------------------------------------------------------------- /10.ratelimit/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/smallnest/dive-to-gosync-workshop/10.ratelimit 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/juju/ratelimit v1.0.1 7 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect 8 | github.com/stretchr/testify v1.6.1 // indirect 9 | go.uber.org/atomic v1.7.0 // indirect 10 | go.uber.org/ratelimit v0.1.0 11 | golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e 12 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect 13 | ) 14 | -------------------------------------------------------------------------------- /1.basic/time/ticker/ticker.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | ticker := time.NewTicker(10 * time.Second) 10 | defer ticker.Stop() 11 | log.Println("create a ticker") 12 | 13 | t := <-ticker.C 14 | log.Println("the first tick:", t.String()) 15 | 16 | time.Sleep(15 * time.Second) 17 | t = <-ticker.C 18 | log.Println("then the second tick:", t.String()) 19 | 20 | t = <-ticker.C 21 | log.Println("then the third tick:", t.String()) 22 | } 23 | -------------------------------------------------------------------------------- /8.pool/workerpool/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | "github.com/gammazero/workerpool" 9 | ) 10 | 11 | func main() { 12 | wp := workerpool.New(20) 13 | 14 | fn := func(i int) { 15 | fmt.Println("Start Job", i) 16 | time.Sleep(time.Duration(i) * time.Second) 17 | fmt.Println("End Job", i) 18 | } 19 | 20 | for i := 0; i < 50; i++ { 21 | i := i 22 | wp.Submit(func() { 23 | fn(i) 24 | }) 25 | } 26 | log.Println("Submitted!") 27 | 28 | wp.StopWait() 29 | } 30 | -------------------------------------------------------------------------------- /1.basic/once/error/once.go: -------------------------------------------------------------------------------- 1 | package sync 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | ) 7 | 8 | type Once struct { 9 | m sync.Mutex 10 | done uint32 11 | } 12 | 13 | func (o *Once) Do(f func() error) error { 14 | if atomic.LoadUint32(&o.done) == 1 { 15 | return nil 16 | } 17 | 18 | return o.slowDo(f) 19 | } 20 | 21 | func (o *Once) slowDo(f func() error) error { 22 | o.m.Lock() 23 | defer o.m.Unlock() 24 | var err error 25 | if o.done == 0 { 26 | err = f() 27 | if err == nil { 28 | atomic.StoreUint32(&o.done, 1) 29 | } 30 | } 31 | return err 32 | } 33 | -------------------------------------------------------------------------------- /1.basic/wg/wg2/waitgroup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | ) 8 | 9 | func main() { 10 | var count int64 11 | var wg sync.WaitGroup 12 | wg.Add(10) 13 | for i := 0; i < 10; i++ { 14 | go func() { 15 | atomic.AddInt64(&count, 1) 16 | wg.Done() 17 | }() 18 | } 19 | wg.Wait() 20 | fmt.Println(atomic.LoadInt64(&count)) 21 | 22 | wg.Add(20) 23 | for i := 0; i < 20; i++ { 24 | go func() { 25 | atomic.AddInt64(&count, 1) 26 | wg.Done() 27 | }() 28 | } 29 | wg.Wait() 30 | fmt.Println(atomic.LoadInt64(&count)) 31 | } 32 | -------------------------------------------------------------------------------- /8.pool/goworkers/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | "github.com/dpaks/goworkers" 9 | ) 10 | 11 | func main() { 12 | opts := goworkers.Options{Workers: 20} 13 | gw := goworkers.New(opts) 14 | 15 | // your actual work 16 | fn := func(i int) { 17 | fmt.Println("Start Job", i) 18 | time.Sleep(time.Duration(i) * time.Second) 19 | fmt.Println("End Job", i) 20 | } 21 | 22 | for i := 0; i < 50; i++ { 23 | i := i 24 | gw.Submit(func() { 25 | fn(i) 26 | }) 27 | } 28 | log.Println("Submitted!") 29 | 30 | gw.Stop(true) 31 | } 32 | -------------------------------------------------------------------------------- /1.basic/wg/wg9/waitgroup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | var wg sync.WaitGroup 11 | 12 | dosomething(100, &wg) 13 | dosomething(110, &wg) 14 | dosomething(120, &wg) 15 | dosomething(130, &wg) 16 | 17 | wg.Wait() 18 | fmt.Println("Done") 19 | } 20 | 21 | func dosomething(millisecs time.Duration, wg *sync.WaitGroup) { 22 | wg.Add(1) 23 | 24 | go func() { 25 | duration := millisecs * time.Millisecond 26 | time.Sleep(duration) 27 | 28 | fmt.Println("后台执行, duration:", duration) 29 | wg.Done() 30 | }() 31 | } 32 | -------------------------------------------------------------------------------- /5.channel/rw/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | var ch = make(chan int, 3) 10 | 11 | var done = make(chan struct{}) 12 | 13 | go func() { 14 | for { 15 | select { 16 | case <-done: 17 | return 18 | case i := <-ch: 19 | fmt.Print(i) 20 | } 21 | } 22 | }() 23 | 24 | go func() { 25 | for i := 0; i < 10; i++ { 26 | select { 27 | case <-done: 28 | return 29 | case ch <- i: 30 | } 31 | 32 | time.Sleep(100 * time.Millisecond) 33 | } 34 | }() 35 | 36 | time.Sleep(2 * time.Second) 37 | } 38 | -------------------------------------------------------------------------------- /9.group/syncs/group.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sync/atomic" 7 | "time" 8 | 9 | "github.com/go-pkgz/syncs" 10 | ) 11 | 12 | func main() { 13 | // 设置并发数是10 14 | swg := syncs.NewSizedGroup(10, syncs.Preemptive) 15 | // swg := syncs.NewSizedGroup(10, syncs.Preemptive) 16 | var c uint32 17 | 18 | // 执行1000个子任务,只会有10个goroutine去执行 19 | for i := 0; i < 1000; i++ { 20 | swg.Go(func(ctx context.Context) { 21 | time.Sleep(5 * time.Millisecond) 22 | atomic.AddUint32(&c, 1) 23 | }) 24 | } 25 | 26 | // 等待任务完成 27 | swg.Wait() 28 | // 输出结果 29 | fmt.Println(c) 30 | } 31 | -------------------------------------------------------------------------------- /1.basic/context/deadline/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | ctx1, c1 := context.WithCancel(context.Background()) 11 | go func() { 12 | fmt.Println("g1 start") 13 | <-ctx1.Done() 14 | fmt.Println("g1 done, err:", ctx1.Err()) 15 | }() 16 | 17 | ctx2, c2 := context.WithDeadline(ctx1, time.Now().Add(time.Hour)) 18 | go func() { 19 | fmt.Println("g2 start") 20 | <-ctx2.Done() 21 | fmt.Println("g2 done, err:", ctx2.Err()) 22 | }() 23 | 24 | time.Sleep(time.Second) 25 | c1() 26 | 27 | time.Sleep(10 * time.Second) 28 | c2() 29 | } 30 | -------------------------------------------------------------------------------- /12.scheduler/trace/trace.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "runtime/trace" 6 | "sync" 7 | ) 8 | 9 | // go tool trace trace.out 10 | func main() { 11 | f, err := os.Create("trace.out") 12 | if err != nil { 13 | panic(err) 14 | } 15 | defer f.Close() 16 | err = trace.Start(f) 17 | if err != nil { 18 | panic(err) 19 | } 20 | defer trace.Stop() 21 | 22 | var wg sync.WaitGroup 23 | 24 | for i := 0; i < 10; i++ { 25 | wg.Add(1) 26 | go func() { 27 | a := 0 28 | 29 | for i := 0; i < 1e7; i++ { 30 | a += 1 31 | } 32 | 33 | wg.Done() 34 | }() 35 | } 36 | 37 | wg.Wait() 38 | } 39 | -------------------------------------------------------------------------------- /8.pool/grpool/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | "github.com/ivpusic/grpool" 9 | ) 10 | 11 | func main() { 12 | pool := grpool.NewPool(20, 50) 13 | pool.WaitCount(50) 14 | 15 | fn := func(i int) { 16 | fmt.Println("Start Job", i) 17 | time.Sleep(time.Duration(i) * time.Second) 18 | fmt.Println("End Job", i) 19 | } 20 | 21 | for i := 0; i < 50; i++ { 22 | i := i 23 | pool.JobQueue <- func() { 24 | fn(i) 25 | } 26 | } 27 | log.Println("Submitted!") 28 | 29 | // wait until we call JobDone for all jobs 30 | pool.WaitAll() 31 | 32 | pool.Release() 33 | } 34 | -------------------------------------------------------------------------------- /1.basic/time/after/after.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | runtime.GC() 11 | mem := &runtime.MemStats{} 12 | runtime.ReadMemStats(mem) 13 | fmt.Println("before:", mem.HeapInuse) 14 | for i := 0; i < 1000; i++ { 15 | task() 16 | } 17 | runtime.GC() 18 | runtime.ReadMemStats(mem) 19 | fmt.Println("after:", mem.HeapInuse) 20 | 21 | time.Sleep(15 * time.Second) 22 | runtime.GC() 23 | runtime.ReadMemStats(mem) 24 | fmt.Println("fired:", mem.HeapInuse) 25 | 26 | } 27 | 28 | func task() { 29 | select { 30 | case <-time.After(10 * time.Second): 31 | fmt.Println("timeout") 32 | return 33 | default: 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /9.group/hunch/all/all.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "time" 8 | 9 | hunch "github.com/aaronjan/hunch" 10 | ) 11 | 12 | func main() { 13 | ctx := context.Background() 14 | r, err := hunch.All( 15 | ctx, 16 | func(ctx context.Context) (any, error) { 17 | time.Sleep(300 * time.Millisecond) 18 | return 1, nil 19 | }, 20 | func(ctx context.Context) (any, error) { 21 | time.Sleep(200 * time.Millisecond) 22 | // return 2, nil 23 | return 0, errors.New("#2 failure") 24 | }, 25 | func(ctx context.Context) (any, error) { 26 | time.Sleep(100 * time.Millisecond) 27 | return 3, nil 28 | }, 29 | ) 30 | 31 | fmt.Println(r, err) 32 | } 33 | -------------------------------------------------------------------------------- /1.basic/cond2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | c := sync.NewCond(&sync.Mutex{}) 12 | 13 | var ready int 14 | 15 | for i := 0; i < 10; i++ { 16 | go func(i int) { 17 | time.Sleep(time.Duration(rand.Int63n(10)) * time.Second) 18 | 19 | // 加锁更改等待条件 20 | c.L.Lock() 21 | ready++ 22 | c.L.Unlock() 23 | 24 | log.Printf("运动员#%d 已准备就绪\n", i) 25 | // 运动员i准备就绪.通知裁判员 26 | c.Broadcast() 27 | }(i) 28 | } 29 | 30 | // c.L.Lock() 31 | for ready != 10 { 32 | c.Wait() 33 | log.Println("裁判员被唤醒一次") 34 | } 35 | // c.L.Unlock() 36 | 37 | //所有的运动员是否就绪 38 | log.Println("所有运动员都准备就绪。比赛开始,3,2,1, ......") 39 | } 40 | -------------------------------------------------------------------------------- /1.basic/once/recover/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | var once sync.Once 12 | 13 | count := 0 14 | go func() { 15 | defer func() { 16 | count++ 17 | recover() 18 | }() 19 | once.Do(func() { 20 | fmt.Println("exec Do") 21 | count = 1 / count 22 | }) 23 | }() 24 | 25 | time.Sleep(time.Second) 26 | 27 | once.Do(func() { 28 | fmt.Println("exec here") 29 | count = 1 / count 30 | }) 31 | 32 | fmt.Println("end") 33 | 34 | addr := "baidu.com" 35 | 36 | var conn net.Conn 37 | var err error 38 | 39 | once.Do(func() { 40 | conn, err = net.Dial("tcp", addr) 41 | }) 42 | 43 | _ = conn 44 | _ = err 45 | } 46 | -------------------------------------------------------------------------------- /2.ext/reentrant_lock/goid/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "strconv" 7 | "strings" 8 | "sync" 9 | ) 10 | 11 | func GoID() int { 12 | var buf [64]byte 13 | n := runtime.Stack(buf[:], false) 14 | idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0] 15 | id, err := strconv.Atoi(idField) 16 | if err != nil { 17 | panic(fmt.Sprintf("cannot get goroutine id: %v", err)) 18 | } 19 | return id 20 | } 21 | func main() { 22 | fmt.Println("main", GoID()) 23 | var wg sync.WaitGroup 24 | for i := 0; i < 10; i++ { 25 | i := i 26 | wg.Add(1) 27 | go func() { 28 | defer wg.Done() 29 | fmt.Println(i, GoID()) 30 | }() 31 | } 32 | wg.Wait() 33 | } 34 | -------------------------------------------------------------------------------- /9.group/gogroup/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "time" 8 | 9 | "github.com/uw-labs/sync/gogroup" 10 | ) 11 | 12 | func main() { 13 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) 14 | defer cancel() 15 | 16 | g, ctx := gogroup.New(ctx) 17 | 18 | g.Go(func() error { 19 | return run(ctx, time.Second) 20 | }) 21 | g.Go(func() error { 22 | time.Sleep(time.Millisecond * 50) 23 | return errors.New("component stopped") 24 | }) 25 | 26 | fmt.Println(g.Wait()) 27 | } 28 | 29 | func run(ctx context.Context, d time.Duration) error { 30 | select { 31 | case <-ctx.Done(): 32 | return ctx.Err() 33 | case <-time.After(d): 34 | return nil 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /5.channel/or_done_channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func orDone(done <-chan struct{}, c <-chan any) <-chan any { 6 | valStream := make(chan any) 7 | go func() { 8 | defer close(valStream) 9 | for { 10 | select { 11 | case <-done: 12 | return 13 | case v, ok := <-c: 14 | if ok == false { 15 | return 16 | } 17 | select { 18 | case valStream <- v: 19 | case <-done: 20 | } 21 | } 22 | } 23 | }() 24 | return valStream 25 | } 26 | 27 | func main() { 28 | ch := make(chan any) 29 | go func() { 30 | defer close(ch) 31 | 32 | for i := 0; i < 10; i++ { 33 | ch <- i 34 | } 35 | }() 36 | 37 | for v := range orDone(nil, ch) { 38 | fmt.Printf("%v ", v) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /2.ext/future/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/reugn/async" 8 | ) 9 | 10 | func main() { 11 | p := async.NewPromise() 12 | go func() { 13 | time.Sleep(time.Millisecond * 100) 14 | p.Success(true) 15 | }() 16 | v, e := p.Future().Get() 17 | fmt.Println(v, e) 18 | 19 | p1 := async.NewPromise() 20 | p2 := async.NewPromise() 21 | p3 := async.NewPromise() 22 | go func() { 23 | time.Sleep(time.Millisecond * 100) 24 | p1.Success(1) 25 | time.Sleep(time.Millisecond * 200) 26 | p2.Success(2) 27 | time.Sleep(time.Millisecond * 300) 28 | p3.Success(3) 29 | }() 30 | 31 | v, e = async.FutureSeq([]async.Future{p1.Future(), p2.Future(), p3.Future()}).Get() 32 | fmt.Println(v, e) 33 | } 34 | -------------------------------------------------------------------------------- /1.basic/mutex/timeout/timeout.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Mutex struct { 8 | ch chan struct{} 9 | } 10 | 11 | func NewMutex() *Mutex { 12 | mu := &Mutex{make(chan struct{}, 1)} 13 | mu.ch <- struct{}{} 14 | return mu 15 | } 16 | func (m *Mutex) Lock() { 17 | <-m.ch 18 | } 19 | func (m *Mutex) Unlock() { 20 | select { 21 | case m.ch <- struct{}{}: 22 | default: 23 | panic("unlock of unlocked mutex") 24 | } 25 | } 26 | func (m *Mutex) TryLock(timeout time.Duration) bool { 27 | timer := time.NewTimer(timeout) 28 | select { 29 | case <-m.ch: 30 | timer.Stop() 31 | return true 32 | case <-timer.C: 33 | } 34 | return false 35 | } 36 | func (m *Mutex) IsLocked() bool { 37 | return len(m.ch) == 0 38 | } 39 | -------------------------------------------------------------------------------- /9.group/syncs2/group.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "sync/atomic" 7 | "time" 8 | 9 | "github.com/go-pkgz/syncs" 10 | ) 11 | 12 | func main() { 13 | // 设置并发数为10,有限的goroutine数 14 | ewg := syncs.NewErrSizedGroup(10, syncs.Preemptive) 15 | var c uint32 16 | 17 | for i := 0; i < 1000; i++ { 18 | i := i 19 | ewg.Go(func() error { 20 | time.Sleep(time.Millisecond * 10) 21 | atomic.AddUint32(&c, 1) 22 | if i == 100 { 23 | return errors.New("err1") 24 | } 25 | if i == 200 { 26 | return errors.New("err2") 27 | } 28 | return nil 29 | }) 30 | } 31 | 32 | // 等待任务完成 33 | if err := ewg.Wait(); err != nil { 34 | fmt.Printf("err: %v\n", err) 35 | } 36 | // 输出结果 37 | fmt.Println(c) 38 | } 39 | -------------------------------------------------------------------------------- /9.group/rungroup/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "time" 8 | 9 | "github.com/uw-labs/sync/rungroup" 10 | ) 11 | 12 | func main() { 13 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) 14 | defer cancel() 15 | 16 | g, ctx := rungroup.New(ctx) 17 | 18 | g.Go(func() error { 19 | return run(ctx, time.Second) 20 | }) 21 | g.Go(func() error { 22 | time.Sleep(time.Millisecond * 50) 23 | return errors.New("component stopped") 24 | }) 25 | 26 | err := g.Wait() 27 | fmt.Println(err) 28 | } 29 | 30 | func run(ctx context.Context, d time.Duration) error { 31 | select { 32 | case <-ctx.Done(): 33 | return ctx.Err() 34 | case <-time.After(d): 35 | return nil 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /1.basic/once/reset/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | ) 7 | 8 | // https://github.com/matryer/resync 9 | 10 | func main() { 11 | } 12 | 13 | type Once struct { 14 | done uint32 15 | m sync.Mutex 16 | } 17 | 18 | func (o *Once) Do(f func()) { 19 | if atomic.LoadUint32(&o.done) == 0 { 20 | // Outlined slow-path to allow inlining of the fast-path. 21 | o.doSlow(f) 22 | } 23 | } 24 | 25 | func (o *Once) doSlow(f func()) { 26 | o.m.Lock() 27 | defer o.m.Unlock() 28 | if o.done == 0 { 29 | defer atomic.StoreUint32(&o.done, 1) 30 | f() 31 | } 32 | } 33 | 34 | // 有意义么?在大部分场景下Reset是无意义的,当然在特殊的场景下可能有意义 35 | func (o *Once) Reset() { 36 | o.m.Lock() 37 | defer o.m.Unlock() 38 | atomic.StoreUint32(&o.done, 0) 39 | } 40 | -------------------------------------------------------------------------------- /1.basic/mutex/syncmap/main.go: -------------------------------------------------------------------------------- 1 | package syncmap 2 | 3 | import "sync" 4 | 5 | type IntMap struct { 6 | m map[int]int 7 | sync.Mutex 8 | } 9 | 10 | func (m *IntMap) Get(k int) (v int, ok bool) { 11 | m.Lock() 12 | defer m.Unlock() 13 | 14 | v, ok = m.m[k] 15 | return v, ok 16 | } 17 | 18 | func (m *IntMap) Set(k, v int) { 19 | m.Lock() 20 | defer m.Unlock() 21 | 22 | m.m[k] = v 23 | } 24 | 25 | func (m *IntMap) Len() int { 26 | m.Lock() 27 | defer m.Unlock() 28 | 29 | return len(m.m) 30 | } 31 | 32 | func (m *IntMap) Range(fn func(k, v int) error) error { 33 | m.Lock() 34 | defer m.Unlock() 35 | 36 | var err error 37 | for k, v := range m.m { 38 | err = fn(k, v) 39 | if err != nil { 40 | return err 41 | } 42 | } 43 | 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /9.group/errgroup/errg.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "log" 6 | "time" 7 | 8 | "golang.org/x/sync/errgroup" 9 | ) 10 | 11 | func main() { 12 | var g errgroup.Group 13 | 14 | // 启动第一个子任务,它执行成功 15 | g.Go(func() error { 16 | time.Sleep(5 * time.Second) 17 | log.Println("exec #1") 18 | return nil 19 | }) 20 | 21 | // 启动第二个子任务,它执行失败 22 | g.Go(func() error { 23 | time.Sleep(10 * time.Second) 24 | log.Println("exec #2") 25 | return errors.New("failed to exec #2") 26 | }) 27 | 28 | // 启动第三个子任务,它执行成功 29 | g.Go(func() error { 30 | time.Sleep(15 * time.Second) 31 | log.Println("exec #3") 32 | return nil 33 | }) 34 | 35 | if err := g.Wait(); err == nil { 36 | log.Println("Successfully exec all") 37 | } else { 38 | log.Println("failed:", err) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /9.group/syncx/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | 8 | "github.com/go-pkgz/syncs" 9 | ) 10 | 11 | func main() { 12 | swg := syncs.NewSizedGroup(5) 13 | for i := 0; i < 10; i++ { 14 | swg.Go(func(ctx context.Context) { 15 | // doThings(ctx) // only 5 of these will run in parallel 16 | }) 17 | } 18 | swg.Wait() 19 | 20 | ewg := syncs.NewErrSizedGroup(5, syncs.Preemptive) // error wait group with max size=5, don't try to start more if any error happened 21 | for i := 0; i < 10; i++ { 22 | ewg.Go(func() error { // Go here could be blocked if trying to run >5 at the same time 23 | // err := doThings(ctx) // only 5 of these will run in parallel 24 | return errors.New("some error") 25 | }) 26 | } 27 | err := ewg.Wait() 28 | fmt.Println(err) 29 | } 30 | -------------------------------------------------------------------------------- /5.channel/trylock/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | "unsafe" 8 | ) 9 | 10 | // defined in sync.Mutex 11 | const mutexLocked = 1 << iota 12 | 13 | type Mutex struct { 14 | mu sync.Mutex 15 | } 16 | 17 | func (m *Mutex) Lock() { 18 | m.mu.Lock() 19 | } 20 | 21 | func (m *Mutex) Unlock() { 22 | m.mu.Unlock() 23 | } 24 | 25 | func (m *Mutex) TryLock() bool { 26 | return atomic.CompareAndSwapInt32((*int32)(unsafe.Pointer(&m.mu)), 0, mutexLocked) 27 | } 28 | 29 | func (m *Mutex) IsLocked() bool { 30 | return atomic.LoadInt32((*int32)(unsafe.Pointer(&m.mu))) == mutexLocked 31 | } 32 | 33 | func main() { 34 | var m Mutex 35 | if m.TryLock() { 36 | fmt.Println("locked: ", m.IsLocked()) 37 | m.Unlock() 38 | } else { 39 | fmt.Println("failed to lock") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /2.ext/cyclicbarrier/cyclicbarrier.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "math/rand" 8 | "sync" 9 | "time" 10 | 11 | "github.com/marusama/cyclicbarrier" 12 | ) 13 | 14 | func main() { 15 | cnt := 0 16 | b := cyclicbarrier.NewWithAction(10, func() error { 17 | cnt++ 18 | return nil 19 | }) 20 | 21 | wg := sync.WaitGroup{} 22 | wg.Add(10) 23 | 24 | for i := 0; i < 10; i++ { 25 | i := i 26 | go func() { 27 | defer wg.Done() 28 | for j := 0; j < 5; j++ { 29 | time.Sleep(time.Duration(rand.Intn(10)) * time.Second) 30 | log.Printf("goroutine %d waits", i) 31 | err := b.Await(context.TODO()) 32 | log.Printf("goroutine %d is OK", i) 33 | if err != nil { 34 | panic(err) 35 | } 36 | } 37 | }() 38 | } 39 | 40 | wg.Wait() 41 | fmt.Println(cnt) 42 | } 43 | -------------------------------------------------------------------------------- /2.ext/reentrant_lock/token_recursive_mutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | ) 8 | 9 | type TokenRecursiveMutex struct { 10 | sync.Mutex 11 | token int64 12 | recursion int32 13 | } 14 | 15 | func (m *TokenRecursiveMutex) Lock(token int64) { 16 | if atomic.LoadInt64(&m.token) == token { 17 | m.recursion++ 18 | return 19 | } 20 | 21 | m.Mutex.Lock() 22 | // we are now inside the lock 23 | atomic.StoreInt64(&m.token, token) 24 | m.recursion = 1 25 | } 26 | 27 | func (m *TokenRecursiveMutex) Unlock(token int64) { 28 | if atomic.LoadInt64(&m.token) != token { 29 | panic(fmt.Sprintf("wrong the owner(%d): %d!", m.token, token)) 30 | } 31 | 32 | m.recursion-- 33 | if m.recursion != 0 { 34 | return 35 | } 36 | 37 | atomic.StoreInt64(&m.token, 0) 38 | m.Mutex.Unlock() 39 | } 40 | -------------------------------------------------------------------------------- /9.group/facebook/errg.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/facebookgo/errgroup" 9 | ) 10 | 11 | func main() { 12 | var g errgroup.Group 13 | g.Add(3) 14 | 15 | // 启动第一个子任务,它执行成功 16 | go func() { 17 | time.Sleep(5 * time.Second) 18 | fmt.Println("exec #1") 19 | g.Done() 20 | }() 21 | 22 | // 启动第二个子任务,它执行失败 23 | go func() { 24 | time.Sleep(10 * time.Second) 25 | fmt.Println("exec #2") 26 | g.Error(errors.New("failed to exec #2")) 27 | g.Done() 28 | }() 29 | 30 | // 启动第三个子任务,它执行成功 31 | go func() { 32 | time.Sleep(15 * time.Second) 33 | fmt.Println("exec #3") 34 | g.Done() 35 | }() 36 | 37 | // 等待所有的goroutine完成,并检查error 38 | if err := g.Wait(); err == nil { 39 | fmt.Println("Successfully exec all") 40 | } else { 41 | fmt.Println("failed:", err) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /1.basic/mutex/value/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | // https://github.com/carlmjohnson/syncx/blob/main/mutex.go 6 | 7 | type Mutex[T any] struct { 8 | mu sync.RWMutex 9 | value T 10 | } 11 | 12 | func NewMutex[T any](initial T) *Mutex[T] { 13 | var m Mutex[T] 14 | m.value = initial 15 | return &m 16 | } 17 | 18 | func (m *Mutex[T]) ReadLock(f func(value T)) { 19 | m.mu.RLock() 20 | defer m.mu.RUnlock() 21 | f(m.value) 22 | } 23 | 24 | func (m *Mutex[T]) Lock(f func(value *T)) { 25 | m.mu.Lock() 26 | defer m.mu.Unlock() 27 | value := m.value 28 | f(&value) 29 | m.value = value 30 | } 31 | 32 | func (m *Mutex[T]) Load() T { 33 | m.mu.RLock() 34 | defer m.mu.RUnlock() 35 | return m.value 36 | } 37 | 38 | func (m *Mutex[T]) Store(value T) { 39 | m.mu.Lock() 40 | defer m.mu.Unlock() 41 | m.value = value 42 | } 43 | -------------------------------------------------------------------------------- /5.channel/trylock_channel2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Mutex struct { 8 | ch chan struct{} 9 | } 10 | 11 | func NewMutex() *Mutex { 12 | mu := &Mutex{make(chan struct{}, 1)} 13 | return mu 14 | } 15 | 16 | func (m *Mutex) Lock() { 17 | m.ch <- struct{}{} 18 | } 19 | 20 | func (m *Mutex) Unlock() { 21 | select { 22 | case <-m.ch: 23 | default: 24 | panic("unlock of unlocked mutex") 25 | } 26 | } 27 | 28 | func (m *Mutex) TryLock() bool { 29 | select { 30 | case m.ch <- struct{}{}: 31 | return true 32 | default: 33 | } 34 | return false 35 | } 36 | 37 | func (m *Mutex) IsLocked() bool { 38 | return len(m.ch) == 1 39 | } 40 | 41 | func main() { 42 | m := NewMutex() 43 | ok := m.TryLock() 44 | fmt.Printf("locked v %v\n", ok) 45 | ok = m.TryLock() 46 | fmt.Printf("locked %v\n", ok) 47 | } 48 | -------------------------------------------------------------------------------- /1.basic/rwmutex/recursive/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | var mu sync.RWMutex 11 | var wg sync.WaitGroup 12 | wg.Add(2) 13 | 14 | // 后面的递归过程中制造一个Lock 15 | go func() { 16 | defer wg.Done() 17 | 18 | time.Sleep(200 * time.Millisecond) 19 | mu.Lock() 20 | fmt.Println("Lock") 21 | time.Sleep(100 * time.Millisecond) 22 | mu.Unlock() 23 | fmt.Println("Unlock") 24 | }() 25 | 26 | go func() { 27 | defer wg.Done() 28 | factorial(&mu, 4) 29 | }() 30 | wg.Wait() 31 | } 32 | 33 | func factorial(m *sync.RWMutex, n int) int { 34 | if n < 1 { 35 | return 0 36 | } 37 | fmt.Println("RLock") 38 | m.RLock() 39 | defer func() { 40 | fmt.Println("RUnlock") 41 | m.RUnlock() 42 | }() 43 | time.Sleep(100 * time.Millisecond) 44 | return factorial(m, n-1) * n 45 | } 46 | -------------------------------------------------------------------------------- /2.ext/reentrant_lock/stacks/main.go: -------------------------------------------------------------------------------- 1 | //GOTRACEBACK=1 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | "os/signal" 8 | "runtime" 9 | "syscall" 10 | "time" 11 | ) 12 | 13 | func main() { 14 | 15 | go func() { 16 | for { 17 | time.Sleep(time.Second) 18 | } 19 | }() 20 | 21 | go func() { 22 | for { 23 | time.Sleep(time.Minute) 24 | } 25 | }() 26 | 27 | time.Sleep(5 * time.Second) 28 | DumpStacks() 29 | 30 | // panic("") 31 | } 32 | 33 | func setupSigusr1Trap() { 34 | c := make(chan os.Signal, 1) 35 | signal.Notify(c, syscall.SIGTERM) 36 | go func() { 37 | for range c { 38 | DumpStacks() 39 | } 40 | }() 41 | } 42 | func DumpStacks() { 43 | buf := make([]byte, 16384) 44 | buf = buf[:runtime.Stack(buf, true)] 45 | fmt.Printf("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf) 46 | } 47 | -------------------------------------------------------------------------------- /3.atomic/pointer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync/atomic" 6 | "unsafe" 7 | ) 8 | 9 | type T struct{ a int } 10 | 11 | func main() { 12 | ppt() 13 | } 14 | 15 | func ppt() { 16 | var pT *T 17 | 18 | var unsafePPT = (*unsafe.Pointer)(unsafe.Pointer(&pT)) 19 | 20 | var ta, tb T 21 | // store 22 | atomic.StorePointer(unsafePPT, unsafe.Pointer(&ta)) 23 | 24 | // load 25 | pa1 := (*T)(atomic.LoadPointer(unsafePPT)) 26 | fmt.Println(pa1 == &ta) // true 27 | 28 | // swap 29 | pa2 := atomic.SwapPointer(unsafePPT, unsafe.Pointer(&tb)) 30 | fmt.Println((*T)(pa2) == &ta) // true 31 | 32 | // compare and swap 33 | b := atomic.CompareAndSwapPointer(unsafePPT, pa2, unsafe.Pointer(&tb)) 34 | fmt.Println(b) // false 35 | b = atomic.CompareAndSwapPointer(unsafePPT, unsafe.Pointer(&tb), pa2) 36 | fmt.Println(b) // true 37 | } 38 | -------------------------------------------------------------------------------- /5.channel/trylock_channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Mutex struct { 8 | ch chan struct{} 9 | } 10 | 11 | func NewMutex() *Mutex { 12 | mu := &Mutex{make(chan struct{}, 1)} 13 | mu.ch <- struct{}{} 14 | return mu 15 | } 16 | 17 | func (m *Mutex) Lock() { 18 | <-m.ch 19 | } 20 | 21 | func (m *Mutex) Unlock() { 22 | select { 23 | case m.ch <- struct{}{}: 24 | default: 25 | panic("unlock of unlocked mutex") 26 | } 27 | } 28 | 29 | func (m *Mutex) TryLock() bool { 30 | select { 31 | case <-m.ch: 32 | return true 33 | default: 34 | } 35 | return false 36 | } 37 | 38 | func (m *Mutex) IsLocked() bool { 39 | return len(m.ch) == 0 40 | } 41 | 42 | func main() { 43 | m := NewMutex() 44 | ok := m.TryLock() 45 | fmt.Printf("locked v %v\n", ok) 46 | ok = m.TryLock() 47 | fmt.Printf("locked %v\n", ok) 48 | } 49 | -------------------------------------------------------------------------------- /9.group/errgroup2/errg.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "time" 8 | 9 | "golang.org/x/sync/errgroup" 10 | ) 11 | 12 | func main() { 13 | g, ctx := errgroup.WithContext(context.Background()) 14 | 15 | g.Go(func() error { 16 | time.Sleep(5 * time.Second) 17 | fmt.Println("exec #1") 18 | return nil 19 | }) 20 | 21 | g.Go(func() error { 22 | time.Sleep(10 * time.Second) 23 | fmt.Println("exec #2") 24 | return errors.New("failed to exec #2") 25 | }) 26 | 27 | g.Go(func() error { 28 | time.Sleep(15 * time.Second) 29 | fmt.Println("exec #3") 30 | return nil 31 | }) 32 | 33 | select { 34 | case <-ctx.Done(): 35 | fmt.Println("done:", ctx.Err()) 36 | } 37 | 38 | if err := g.Wait(); err == nil { 39 | fmt.Println("Successfully exec all") 40 | } else { 41 | fmt.Println("Successfully exec:", err) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /1.basic/wg/wg8/waitgroup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | // https://stackoverflow.com/questions/19208725/example-for-sync-waitgroup-correct 10 | func main() { 11 | var wg sync.WaitGroup 12 | wg.Add(4) 13 | go dosomething2(100, &wg) 14 | go dosomething2(110, &wg) 15 | go dosomething2(120, &wg) 16 | go dosomething2(130, &wg) 17 | 18 | wg.Wait() 19 | fmt.Println("Done") 20 | } 21 | 22 | func dosomething(millisecs time.Duration, wg *sync.WaitGroup) { 23 | duration := millisecs * time.Millisecond 24 | time.Sleep(duration) 25 | fmt.Println("Function in background, duration:", duration) 26 | wg.Done() 27 | } 28 | 29 | func dosomething2(millisecs time.Duration, wg *sync.WaitGroup) { 30 | defer wg.Done() 31 | duration := millisecs * time.Millisecond 32 | time.Sleep(duration) 33 | fmt.Println("Function in background, duration:", duration) 34 | } 35 | -------------------------------------------------------------------------------- /7.orchestration/water3/water.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | import ( 4 | "context" 5 | 6 | "golang.org/x/sync/semaphore" 7 | ) 8 | 9 | type H2O struct { 10 | semaH *semaphore.Weighted 11 | semaO *semaphore.Weighted 12 | } 13 | 14 | func New() *H2O { 15 | semaO := semaphore.NewWeighted(2) 16 | semaO.Acquire(context.Background(), 2) 17 | 18 | return &H2O{ 19 | semaH: semaphore.NewWeighted(2), 20 | semaO: semaO, 21 | } 22 | } 23 | 24 | func (h2o *H2O) hydrogen(releaseHydrogen func()) { 25 | h2o.semaH.Acquire(context.Background(), 1) 26 | 27 | // releaseHydrogen() outputs "H". Do not change or remove this line. 28 | releaseHydrogen() 29 | 30 | h2o.semaO.Release(1) 31 | } 32 | 33 | func (h2o *H2O) oxygen(releaseOxygen func()) { 34 | h2o.semaO.Acquire(context.Background(), 2) 35 | 36 | // releaseOxygen() outputs "O". Do not change or remove this line. 37 | releaseOxygen() 38 | 39 | h2o.semaH.Release(2) 40 | } 41 | -------------------------------------------------------------------------------- /1.basic/context/cancel2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | _ "net/http/pprof" 7 | "time" 8 | ) 9 | 10 | // parent的cancel可以取消子context. 11 | // 子context只能取消自己和它的子. 12 | // 子会propagateCancel,一直上上找,直到找到一个可cancel的context, 把自己加到它的children中. 13 | func main() { 14 | ctx := context.Background() 15 | 16 | ctx1, c1 := context.WithCancel(ctx) 17 | go func() { 18 | fmt.Println("g1 start") 19 | <-ctx1.Done() 20 | fmt.Println("g1 done, err:", ctx1.Err()) 21 | }() 22 | 23 | ctx2 := context.WithValue(ctx1, "aaa", 1) 24 | go func() { 25 | fmt.Println("g2 start") 26 | <-ctx2.Done() 27 | fmt.Println("g2 done, err:", ctx1.Err()) 28 | }() 29 | 30 | ctx3, c3 := context.WithCancel(ctx2) 31 | go func() { 32 | fmt.Println("g3 start") 33 | <-ctx3.Done() 34 | fmt.Println("g3 done, err:", ctx1.Err()) 35 | }() 36 | 37 | time.Sleep(1e9) 38 | c1() 39 | time.Sleep(5 * time.Second) 40 | c3() 41 | 42 | } 43 | -------------------------------------------------------------------------------- /2.ext/reentrant_lock/recursive_mutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | 8 | "github.com/petermattis/goid" 9 | ) 10 | 11 | func main() { 12 | 13 | } 14 | 15 | // RecursiveLock aka. ReentrantLock 16 | type RecursiveMutex struct { 17 | sync.Mutex 18 | owner int64 19 | recursion int32 20 | } 21 | 22 | func (m *RecursiveMutex) Lock() { 23 | gid := goid.Get() 24 | if atomic.LoadInt64(&m.owner) == gid { 25 | m.recursion++ 26 | return 27 | } 28 | m.Mutex.Lock() 29 | // we are now inside the lock 30 | atomic.StoreInt64(&m.owner, gid) 31 | m.recursion = 1 32 | } 33 | 34 | func (m *RecursiveMutex) Unlock() { 35 | gid := goid.Get() 36 | if atomic.LoadInt64(&m.owner) != gid { 37 | panic(fmt.Sprintf("wrong the owner(%d): %d!", m.owner, gid)) 38 | } 39 | m.recursion-- 40 | if m.recursion != 0 { 41 | return 42 | } 43 | atomic.StoreInt64(&m.owner, -1) 44 | m.Mutex.Unlock() 45 | } 46 | -------------------------------------------------------------------------------- /5.channel/or_channel_go/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func or(chans ...<-chan any) <-chan any { 10 | out := make(chan any) 11 | go func() { 12 | var once sync.Once 13 | for _, c := range chans { 14 | go func(c <-chan any) { 15 | select { 16 | case <-c: 17 | once.Do(func() { close(out) }) 18 | case <-out: 19 | } 20 | }(c) 21 | } 22 | }() 23 | return out 24 | } 25 | 26 | func main() { 27 | sig := func(after time.Duration) <-chan any { 28 | c := make(chan any) 29 | go func() { 30 | defer close(c) 31 | time.Sleep(after) 32 | }() 33 | return c 34 | } 35 | 36 | start := time.Now() 37 | 38 | <-or( 39 | sig(10*time.Second), 40 | sig(10*time.Second), 41 | sig(10*time.Second), 42 | sig(10*time.Second), 43 | sig(10*time.Second), 44 | sig(01*time.Minute), 45 | ) 46 | 47 | fmt.Printf("done after %v", time.Since(start)) 48 | } 49 | -------------------------------------------------------------------------------- /1.basic/cond/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | // https://stackoverflow.com/questions/51371587/broadcast-in-golang 11 | 12 | func main() { 13 | var m sync.Mutex 14 | c := sync.NewCond(&m) 15 | 16 | ready := make(chan struct{}, 10) 17 | isReady := false 18 | 19 | for i := 0; i < 10; i++ { 20 | i := i 21 | go func() { 22 | m.Lock() 23 | 24 | time.Sleep(time.Duration(rand.Int63n(2)) * time.Second) 25 | 26 | ready <- struct{}{} // 运动员i准备就绪 27 | for !isReady { 28 | c.Wait() 29 | } 30 | log.Printf("%d started\n", i) 31 | m.Unlock() 32 | }() 33 | } 34 | 35 | // false broadcast 36 | c.Broadcast() 37 | 38 | // 裁判员检查所有的运动员是否就绪 39 | for i := 0; i < 10; i++ { 40 | <-ready 41 | } 42 | 43 | // 运动员都已准备就绪,发令枪响, broadcast 44 | // m.Lock() 45 | isReady = true 46 | c.Broadcast() 47 | // m.Unlock() 48 | 49 | time.Sleep(time.Second) 50 | } 51 | -------------------------------------------------------------------------------- /1.basic/map/rwmap/rwmap.go: -------------------------------------------------------------------------------- 1 | package rwmap 2 | 3 | import "sync" 4 | 5 | type RWMap struct { 6 | sync.RWMutex 7 | m map[int]int 8 | } 9 | 10 | func NewRWMap(n int) *RWMap { 11 | return &RWMap{ 12 | m: make(map[int]int, n), 13 | } 14 | } 15 | func (m *RWMap) Get(k int) (int, bool) { 16 | m.RLock() 17 | defer m.RUnlock() 18 | 19 | v, existed := m.m[k] 20 | return v, existed 21 | } 22 | 23 | func (m *RWMap) Set(k int, v int) { 24 | m.Lock() 25 | defer m.Unlock() 26 | 27 | m.m[k] = v 28 | } 29 | 30 | func (m *RWMap) Delete(k int) { 31 | m.Lock() 32 | defer m.Unlock() 33 | 34 | delete(m.m, k) 35 | } 36 | 37 | func (m *RWMap) Len() int { 38 | m.RLock() 39 | defer m.RUnlock() 40 | 41 | return len(m.m) 42 | } 43 | 44 | func (m *RWMap) Each(f func(k, v int) bool) { 45 | m.RLock() 46 | defer m.RUnlock() 47 | 48 | for k, v := range m.m { 49 | if !f(k, v) { 50 | return 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /9.group/bilibili/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sync/atomic" 7 | "time" 8 | 9 | "github.com/go-kratos/kratos/pkg/sync/errgroup" 10 | ) 11 | 12 | func main() { 13 | var g errgroup.Group 14 | g.GOMAXPROCS(1) // 只使用一个goroutine处理子任务 15 | 16 | var count int64 17 | g.Go(func(ctx context.Context) error { 18 | time.Sleep(time.Second) // 睡眠5秒,把这个goroutine占住 19 | return nil 20 | }) 21 | 22 | total := 10000 23 | 24 | for i := 0; i < total; i++ { // 并发一万个goroutine执行子任务,理论上这些子任务都会加入到Group的待处理列表中 25 | go func() { 26 | g.Go(func(ctx context.Context) error { 27 | atomic.AddInt64(&count, 1) 28 | return nil 29 | }) 30 | }() 31 | } 32 | 33 | // 等待所有的子任务完成。理论上10001个子任务都会被完成 34 | if err := g.Wait(); err != nil { 35 | panic(err) 36 | } 37 | 38 | got := atomic.LoadInt64(&count) 39 | if got != int64(total) { 40 | panic(fmt.Sprintf("expect %d but got %d", total, got)) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /1.basic/wg/wg1/waitgroup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | // https://go101.org/article/concurrent-synchronization-more.html 10 | // http://goinbigdata.com/golang-wait-for-all-goroutines-to-finish/ 11 | // https://golang.org/pkg/sync/#example_WaitGroup 12 | 13 | // correct or wrong usage 14 | 15 | type Counter struct { 16 | mu sync.Mutex 17 | count uint64 18 | } 19 | 20 | func (c *Counter) Incr() { 21 | c.mu.Lock() 22 | c.count++ 23 | c.mu.Unlock() 24 | } 25 | 26 | func (c *Counter) Count() uint64 { 27 | c.mu.Lock() 28 | defer c.mu.Unlock() 29 | return c.count 30 | } 31 | 32 | func worker(c *Counter, wg *sync.WaitGroup) { 33 | defer wg.Done() 34 | time.Sleep(time.Second) 35 | c.Incr() 36 | } 37 | 38 | func main() { 39 | var counter Counter 40 | var wg sync.WaitGroup 41 | wg.Add(10) 42 | 43 | for i := 0; i < 10; i++ { 44 | go worker(&counter, &wg) 45 | } 46 | wg.Wait() 47 | 48 | fmt.Println(counter.Count()) 49 | } 50 | -------------------------------------------------------------------------------- /9.group/errgroup3/errg.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | 8 | "golang.org/x/sync/errgroup" 9 | ) 10 | 11 | func main() { 12 | var g errgroup.Group 13 | var result = make([]error, 3) 14 | 15 | // 启动第一个子任务,它执行成功 16 | g.Go(func() error { 17 | time.Sleep(5 * time.Second) 18 | fmt.Println("exec #1") 19 | result[0] = nil // 保存成功或者失败的结果 20 | return nil 21 | }) 22 | 23 | // 启动第二个子任务,它执行失败 24 | g.Go(func() error { 25 | time.Sleep(10 * time.Second) 26 | fmt.Println("exec #2") 27 | 28 | result[1] = errors.New("failed to exec #2") // 保存成功或者失败的结果 29 | return result[1] 30 | }) 31 | 32 | // 启动第三个子任务,它执行成功 33 | g.Go(func() error { 34 | time.Sleep(15 * time.Second) 35 | fmt.Println("exec #3") 36 | result[2] = nil // 保存成功或者失败的结果 37 | return nil 38 | }) 39 | 40 | if err := g.Wait(); err == nil { 41 | fmt.Printf("Successfully exec all. result: %v\n", result) 42 | } else { 43 | fmt.Printf("failed: %v\n", result) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /5.channel/trylock_timeout/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type Mutex struct { 9 | ch chan struct{} 10 | } 11 | 12 | func NewMutex() *Mutex { 13 | mu := &Mutex{make(chan struct{}, 1)} 14 | mu.ch <- struct{}{} 15 | return mu 16 | } 17 | 18 | func (m *Mutex) Lock() { 19 | <-m.ch 20 | } 21 | 22 | func (m *Mutex) Unlock() { 23 | select { 24 | case m.ch <- struct{}{}: 25 | default: 26 | panic("unlock of unlocked mutex") 27 | } 28 | } 29 | 30 | func (m *Mutex) TryLock(timeout time.Duration) bool { 31 | timer := time.NewTimer(timeout) 32 | select { 33 | case <-m.ch: 34 | timer.Stop() 35 | return true 36 | case <-time.After(timeout): 37 | } 38 | return false 39 | } 40 | 41 | func (m *Mutex) IsLocked() bool { 42 | return len(m.ch) == 0 43 | } 44 | 45 | func main() { 46 | m := NewMutex() 47 | ok := m.TryLock(time.Second) 48 | fmt.Printf("locked v %v\n", ok) 49 | ok = m.TryLock(time.Second) 50 | fmt.Printf("locked %v\n", ok) 51 | } 52 | -------------------------------------------------------------------------------- /2.ext/spinlock/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "sync/atomic" 7 | "time" 8 | ) 9 | 10 | type SpinLock struct { 11 | f uint32 12 | } 13 | 14 | func (sl *SpinLock) Lock() { 15 | for !sl.TryLock() { 16 | runtime.Gosched() 17 | } 18 | } 19 | 20 | func (sl *SpinLock) Unlock() { 21 | atomic.StoreUint32(&sl.f, 0) 22 | } 23 | 24 | func (sl *SpinLock) TryLock() bool { 25 | return atomic.CompareAndSwapUint32(&sl.f, 0, 1) 26 | } 27 | 28 | func (sl *SpinLock) String() string { 29 | if atomic.LoadUint32(&sl.f) == 1 { 30 | return "Locked" 31 | } 32 | return "Unlocked" 33 | } 34 | 35 | func main() { 36 | var mu = &SpinLock{} 37 | go func() { 38 | mu.Lock() 39 | time.Sleep(time.Second) 40 | mu.Unlock() 41 | }() 42 | 43 | time.Sleep(time.Second) 44 | 45 | ok := mu.TryLock() 46 | if ok { 47 | fmt.Println("got the lock") 48 | // do something 49 | mu.Unlock() 50 | return 51 | } else { 52 | fmt.Println("can't get the lock") 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /2.ext/optimistic_lock/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | 8 | "github.com/reugn/async" 9 | ) 10 | 11 | func main() { 12 | 13 | var sv = &syncValue{l: async.NewOptimisticLock()} 14 | var wg sync.WaitGroup 15 | wg.Add(51) 16 | 17 | // read 18 | for i := 0; i < 50; i++ { 19 | go func() { 20 | for i := 0; i < 100; i++ { 21 | fmt.Println("read: ", sv.read()) 22 | } 23 | wg.Done() 24 | }() 25 | } 26 | 27 | //write 28 | go func() { 29 | for i := 0; i < 50; i++ { 30 | sv.incr() 31 | time.Sleep(time.Millisecond) 32 | } 33 | wg.Done() 34 | }() 35 | 36 | wg.Wait() 37 | } 38 | 39 | type syncValue struct { 40 | v int 41 | l *async.OptimisticLock 42 | } 43 | 44 | func (sv *syncValue) read() int { 45 | var v int 46 | ok := false 47 | for !ok { 48 | stamp := sv.l.OptLock() 49 | v = sv.v 50 | ok = sv.l.OptUnlock(stamp) 51 | } 52 | 53 | return v 54 | } 55 | 56 | func (sv *syncValue) incr() { 57 | sv.l.Lock() 58 | sv.v++ 59 | sv.l.Unlock() 60 | } 61 | -------------------------------------------------------------------------------- /2.ext/semaphore/sema.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "runtime" 8 | "time" 9 | 10 | "golang.org/x/sync/semaphore" 11 | ) 12 | 13 | var ( 14 | maxWorkers = runtime.GOMAXPROCS(0) // worker数量 15 | sema = semaphore.NewWeighted(int64(maxWorkers)) //信号量 16 | task = make([]int, maxWorkers*4) // 任务数,是worker的四倍 17 | ) 18 | 19 | func main() { 20 | ctx := context.Background() 21 | 22 | for i := range task { 23 | // 如果没有worker可用,会阻塞在这里,直到某个worker被释放 24 | if err := sema.Acquire(ctx, 1); err != nil { 25 | break 26 | } 27 | 28 | // 启动worker goroutine 29 | go func(i int) { 30 | defer sema.Release(1) 31 | time.Sleep(100 * time.Millisecond) // 模拟一个耗时操作 32 | task[i] = i + 1 33 | }(i) 34 | } 35 | 36 | // 请求所有的worker,这样能确保前面的worker都执行完 37 | if err := sema.Acquire(ctx, int64(maxWorkers)); err != nil { 38 | log.Printf("获取所有的worker失败: %v", err) 39 | } 40 | 41 | fmt.Println(task) 42 | } 43 | -------------------------------------------------------------------------------- /7.orchestration/water/water.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/marusama/cyclicbarrier" 7 | "golang.org/x/sync/semaphore" 8 | ) 9 | 10 | type H2O struct { 11 | semaH *semaphore.Weighted 12 | semaO *semaphore.Weighted 13 | b cyclicbarrier.CyclicBarrier 14 | } 15 | 16 | func New() *H2O { 17 | return &H2O{ 18 | semaH: semaphore.NewWeighted(2), 19 | semaO: semaphore.NewWeighted(1), 20 | b: cyclicbarrier.New(3), 21 | } 22 | } 23 | 24 | func (h2o *H2O) hydrogen(releaseHydrogen func()) { 25 | h2o.semaH.Acquire(context.Background(), 1) 26 | 27 | // releaseHydrogen() outputs "H". Do not change or remove this line. 28 | releaseHydrogen() 29 | h2o.b.Await(context.Background()) 30 | 31 | h2o.semaH.Release(1) 32 | } 33 | 34 | func (h2o *H2O) oxygen(releaseOxygen func()) { 35 | h2o.semaO.Acquire(context.Background(), 1) 36 | 37 | // releaseOxygen() outputs "O". Do not change or remove this line. 38 | releaseOxygen() 39 | h2o.b.Await(context.Background()) 40 | 41 | h2o.semaO.Release(1) 42 | } 43 | -------------------------------------------------------------------------------- /6.happenbefore/double-checking/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | "time" 7 | ) 8 | 9 | // https://wiki.sei.cmu.edu/confluence/display/java/LCK10-J.+Use+a+correct+form+of+the+double-checked+locking+idiom 10 | // https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html 11 | 12 | var a uint64 13 | var mu sync.Mutex 14 | 15 | func main() { 16 | go foo() 17 | go foo() 18 | 19 | // go fooByAtomic() 20 | // go fooByAtomic() 21 | 22 | // go fooByMoreScope() 23 | // go fooByMoreScope() 24 | 25 | time.Sleep(time.Second) 26 | } 27 | 28 | func foo() { 29 | if a == 1 { 30 | return 31 | } 32 | 33 | mu.Lock() 34 | if a == 0 { 35 | a = 1 36 | } 37 | mu.Unlock() 38 | } 39 | 40 | func fooByAtomic() { 41 | if atomic.LoadUint64(&a) == 1 { 42 | return 43 | } 44 | 45 | mu.Lock() 46 | if a == 0 { 47 | atomic.StoreUint64(&a, 1) 48 | } 49 | mu.Unlock() 50 | } 51 | 52 | func fooByMoreScope() { 53 | mu.Lock() 54 | defer mu.Unlock() 55 | 56 | if a == 1 { 57 | return 58 | } 59 | a = 1 60 | } 61 | -------------------------------------------------------------------------------- /7.orchestration/water2/water.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | 7 | "golang.org/x/sync/semaphore" 8 | ) 9 | 10 | type H2O struct { 11 | semaH *semaphore.Weighted 12 | semaO *semaphore.Weighted 13 | wg sync.WaitGroup 14 | } 15 | 16 | func New() *H2O { 17 | var wg sync.WaitGroup 18 | wg.Add(3) 19 | 20 | return &H2O{ 21 | semaH: semaphore.NewWeighted(2), 22 | semaO: semaphore.NewWeighted(1), 23 | wg: wg, 24 | } 25 | } 26 | 27 | func (h2o *H2O) hydrogen(releaseHydrogen func()) { 28 | h2o.semaH.Acquire(context.Background(), 1) 29 | 30 | // releaseHydrogen() outputs "H". Do not change or remove this line. 31 | releaseHydrogen() 32 | 33 | // wait 34 | h2o.wg.Done() 35 | h2o.wg.Wait() 36 | 37 | h2o.semaH.Release(1) 38 | } 39 | 40 | func (h2o *H2O) oxygen(releaseOxygen func()) { 41 | h2o.semaO.Acquire(context.Background(), 1) 42 | 43 | // releaseOxygen() outputs "O". Do not change or remove this line. 44 | releaseOxygen() 45 | 46 | // wait and reset 47 | h2o.wg.Done() 48 | h2o.wg.Wait() 49 | h2o.wg.Add(3) 50 | 51 | h2o.semaO.Release(1) 52 | } 53 | -------------------------------------------------------------------------------- /1.basic/context/cancel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | _ "net/http/pprof" 7 | "time" 8 | ) 9 | 10 | // parent的cancel可以取消子context. 11 | // 子context只能取消自己和它的子. 12 | // 子会propagateCancel,一直上上找,直到找到一个可cancel的context, 把自己加到它的children中. 13 | func main() { 14 | ctx := context.Background() 15 | 16 | ctx1, c1 := context.WithCancel(ctx) 17 | go func() { 18 | fmt.Println("g1 start") 19 | <-ctx1.Done() 20 | fmt.Println("g1 done, err:", ctx1.Err()) 21 | }() 22 | 23 | ctx2, c2 := context.WithCancel(ctx1) 24 | go func() { 25 | fmt.Println("g2 start") 26 | <-ctx2.Done() 27 | fmt.Println("g2 done, err:", ctx1.Err()) 28 | }() 29 | 30 | ctx3, c3 := context.WithCancel(ctx2) 31 | go func() { 32 | fmt.Println("g3 start") 33 | <-ctx3.Done() 34 | fmt.Println("g3 done, err:", ctx1.Err()) 35 | }() 36 | 37 | time.Sleep(1e9) 38 | c1() 39 | time.Sleep(5 * time.Second) 40 | c2() 41 | time.Sleep(5 * time.Second) 42 | c3() 43 | 44 | // time.Sleep(1e9) 45 | // c3() 46 | // time.Sleep(5 * time.Second) 47 | // c2() 48 | // time.Sleep(5 * time.Second) 49 | // c1() 50 | 51 | } 52 | -------------------------------------------------------------------------------- /3.atomic/asm/asm.sh: -------------------------------------------------------------------------------- 1 | # go tool dist list 2 | 3 | # sudo ./buildpkg.sh darwin amd64 4 | # sudo ./buildpkg.sh windows 386 5 | # sudo ./buildpkg.sh windows arm 6 | # sudo ./buildpkg.sh linux arm64 7 | # sudo ./buildpkg.sh linux mips 8 | # sudo ./buildpkg.sh linux mips64 9 | 10 | 11 | 指令后缀的意义: 12 | 13 | ```go 14 | // 后缀 https://en.wikibooks.org/wiki/X86_Assembly/GAS_Syntax#Operation_Suffixes 15 | // 16 | // b = byte (8 bit) 17 | // s = single (32-bit floating point) 18 | // w = word (16 bit) 19 | // l = long (32 bit integer or 64-bit floating point) 20 | // q = quad (64 bit) 21 | // t = ten bytes (80-bit floating point) 22 | ``` 23 | 24 | ## darwin/amd64 25 | GOOS=darwin GOARCH=amd64 go tool compile -S main.go 26 | 27 | ## windows/386 28 | GOOS=windows GOARCH=386 go tool compile -S main.go 29 | 30 | ## windows arm 31 | GOOS=windows GOARCH=arm go tool compile -S main.go 32 | 33 | ## linux arm64 34 | GOOS=linux GOARCH=arm64 go tool compile -S main.go 35 | 36 | ## linux mips 37 | GOOS=linux GOARCH=mips go tool compile -S main.go 38 | 39 | ## linux mips64 40 | GOOS=linux GOARCH=mips64 go tool compile -S main.go 41 | 42 | -------------------------------------------------------------------------------- /5.channel/or_channel_reflect/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "time" 7 | ) 8 | 9 | func or(channels ...<-chan any) <-chan any { 10 | switch len(channels) { 11 | case 0: 12 | return nil 13 | case 1: 14 | return channels[0] 15 | } 16 | 17 | orDone := make(chan any) 18 | go func() { 19 | defer close(orDone) 20 | var cases []reflect.SelectCase 21 | for _, c := range channels { 22 | cases = append(cases, reflect.SelectCase{ 23 | Dir: reflect.SelectRecv, 24 | Chan: reflect.ValueOf(c), 25 | }) 26 | } 27 | 28 | reflect.Select(cases) 29 | }() 30 | 31 | return orDone 32 | } 33 | 34 | func sig(after time.Duration) <-chan any { 35 | c := make(chan any) 36 | go func() { 37 | defer close(c) 38 | time.Sleep(after) 39 | }() 40 | return c 41 | } 42 | 43 | func main() { 44 | 45 | start := time.Now() 46 | 47 | <-or( 48 | sig(10*time.Second), 49 | sig(20*time.Second), 50 | sig(30*time.Second), 51 | sig(40*time.Second), 52 | sig(50*time.Second), 53 | sig(01*time.Minute), 54 | ) 55 | 56 | fmt.Printf("done after %v", time.Since(start)) 57 | } 58 | -------------------------------------------------------------------------------- /1.basic/rwmutex/readwrite/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | var wg sync.WaitGroup 11 | wg.Add(12) 12 | 13 | var mu sync.RWMutex 14 | 15 | // 读锁 16 | go func() { 17 | defer wg.Done() 18 | 19 | mu.RLock() 20 | fmt.Println("before lock, reader0 RLock") 21 | time.Sleep(10 * time.Second) 22 | mu.RUnlock() 23 | fmt.Println("before lock,, reader0 RUnlock") 24 | }() 25 | 26 | time.Sleep(2 * time.Second) 27 | 28 | // 写锁 29 | go func() { 30 | defer wg.Done() 31 | 32 | mu.Lock() 33 | fmt.Println("writer1 Lock") 34 | time.Sleep(10 * time.Second) 35 | mu.Unlock() 36 | fmt.Println("writer1 Unlock") 37 | }() 38 | 39 | time.Sleep(time.Second) 40 | 41 | // 被前面的写锁 block 42 | for i := 1; i <= 10; i++ { 43 | i := i 44 | go func() { 45 | defer wg.Done() 46 | 47 | mu.RLock() 48 | fmt.Printf("after lock, reader %d RLock\n", i) 49 | time.Sleep(5 * time.Second) 50 | mu.RUnlock() 51 | fmt.Printf("after lock, reader %d RUnlock\n", i) 52 | }() 53 | } 54 | 55 | time.Sleep(time.Second) 56 | 57 | wg.Wait() 58 | } 59 | -------------------------------------------------------------------------------- /10.ratelimit/x/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "golang.org/x/time/rate" 9 | ) 10 | 11 | func main() { 12 | limit() 13 | fmt.Println() 14 | 15 | burst() 16 | fmt.Println() 17 | 18 | reservation() 19 | fmt.Println() 20 | } 21 | 22 | func limit() { 23 | fmt.Println("run limit") 24 | rl := rate.NewLimiter(100, 1) 25 | 26 | for i := 0; i < 10; i++ { 27 | start := time.Now() 28 | _ = rl.Wait(context.Background()) 29 | fmt.Printf("#%d, took: %v\n", i, time.Since(start)) 30 | } 31 | } 32 | 33 | func burst() { 34 | fmt.Println("run burst") 35 | rl := rate.NewLimiter(100, 2) 36 | 37 | for i := 0; i < 10; i++ { 38 | start := time.Now() 39 | _ = rl.WaitN(context.Background(), 2) // return err if burst > 2 40 | fmt.Printf("#%d, took: %v\n", i, time.Since(start)) 41 | } 42 | } 43 | 44 | func reservation() { 45 | fmt.Println("run reservation") 46 | rl := rate.NewLimiter(10, 100) 47 | rl.WaitN(context.Background(), 100) 48 | 49 | re := rl.ReserveN(time.Now(), 50) 50 | if !re.OK() { 51 | return 52 | } 53 | 54 | fmt.Println("delay:", re.Delay()) 55 | } 56 | -------------------------------------------------------------------------------- /9.group/schedgroup/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sync" 7 | "time" 8 | 9 | "github.com/mdlayher/schedgroup" 10 | ) 11 | 12 | func main() { 13 | sg := schedgroup.New(context.Background()) 14 | 15 | n := 5 16 | spread := 100 * time.Millisecond 17 | 18 | timeC := make(chan time.Time, n) 19 | 20 | var wg sync.WaitGroup 21 | wg.Add(1) 22 | defer wg.Wait() 23 | 24 | go func() { 25 | defer func() { 26 | close(timeC) 27 | wg.Done() 28 | }() 29 | 30 | // Produce the current time when a task is fired. 31 | for i := 0; i < n; i++ { 32 | sg.Delay(time.Duration(i+1)*spread, func() { 33 | timeC <- time.Now() 34 | }) 35 | } 36 | 37 | if err := sg.Wait(); err != nil { 38 | panicf("failed to wait: %v", err) 39 | } 40 | }() 41 | 42 | var last time.Time 43 | var recv int 44 | 45 | for tv := range timeC { 46 | recv++ 47 | 48 | if !last.IsZero() { 49 | diff := tv.Sub(last) 50 | fmt.Printf("executed a task since %v\n", diff) 51 | } 52 | 53 | last = tv 54 | } 55 | } 56 | 57 | func panicf(format string, a ...any) { 58 | panic(fmt.Sprintf(format, a...)) 59 | } 60 | -------------------------------------------------------------------------------- /5.channel/or_channel_rec/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func or(channels ...<-chan any) <-chan any { 9 | switch len(channels) { 10 | case 0: 11 | return nil 12 | case 1: 13 | return channels[0] 14 | } 15 | 16 | orDone := make(chan any) 17 | go func() { 18 | defer close(orDone) 19 | 20 | switch len(channels) { 21 | case 2: 22 | select { 23 | case <-channels[0]: 24 | case <-channels[1]: 25 | } 26 | default: 27 | m := len(channels) / 2 28 | select { 29 | case <-or(append(channels[:m:m], orDone)...): // must append orDone to avoid leak!!!! 30 | case <-or(append(channels[m:], orDone)...): 31 | } 32 | } 33 | }() 34 | 35 | return orDone 36 | } 37 | 38 | func sig(after time.Duration) <-chan any { 39 | c := make(chan any) 40 | go func() { 41 | defer close(c) 42 | time.Sleep(after) 43 | }() 44 | return c 45 | } 46 | 47 | func main() { 48 | start := time.Now() 49 | 50 | <-or( 51 | sig(10*time.Second), 52 | sig(20*time.Second), 53 | sig(30*time.Second), 54 | sig(40*time.Second), 55 | sig(50*time.Second), 56 | sig(01*time.Minute), 57 | ) 58 | 59 | fmt.Printf("done after %v", time.Since(start)) 60 | } 61 | -------------------------------------------------------------------------------- /10.ratelimit/juju/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/juju/ratelimit" 8 | ) 9 | 10 | func main() { 11 | limit() 12 | fmt.Println() 13 | 14 | limitWithQuantum() 15 | fmt.Println() 16 | 17 | limitWithRate() 18 | fmt.Println() 19 | } 20 | 21 | func limit() { 22 | rl := ratelimit.NewBucket(10*time.Millisecond, 1) // 1/10ms 23 | fmt.Println("run limit, rate:", rl.Rate()) 24 | 25 | for i := 0; i < 10; i++ { 26 | start := time.Now() 27 | rl.Wait(1) 28 | fmt.Printf("#%d, took: %v\n", i, time.Since(start)) 29 | } 30 | } 31 | 32 | func limitWithQuantum() { 33 | rl := ratelimit.NewBucketWithQuantum(100*time.Millisecond, 1, 10) // 10/100ms, refill per 100*time.Millisecond 34 | fmt.Println("run limitWithQuantum, rate:", rl.Rate()) 35 | 36 | for i := 0; i < 10; i++ { 37 | start := time.Now() 38 | rl.Wait(1) 39 | fmt.Printf("#%d, took: %v\n", i, time.Since(start)) 40 | } 41 | } 42 | 43 | func limitWithRate() { 44 | rl := ratelimit.NewBucketWithRate(100, 1) // 100/s 45 | fmt.Println("run limitWithRate, rate:", rl.Rate()) 46 | 47 | for i := 0; i < 10; i++ { 48 | start := time.Now() 49 | rl.Wait(1) 50 | fmt.Printf("#%d, took: %v\n", i, time.Since(start)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /5.channel/mapreduce/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func mapChan[T, K any](in <-chan T, fn func(T) K) <-chan K { 6 | out := make(chan K) 7 | if in == nil { 8 | close(out) 9 | return out 10 | } 11 | 12 | go func() { 13 | defer close(out) 14 | 15 | for v := range in { 16 | out <- fn(v) 17 | } 18 | }() 19 | 20 | return out 21 | } 22 | 23 | func reduce[T, K any](in <-chan T, fn func(r K, v T) K) K { 24 | var out K 25 | 26 | if in == nil { 27 | return out 28 | } 29 | 30 | for v := range in { 31 | out = fn(out, v) 32 | } 33 | 34 | return out 35 | } 36 | 37 | func asStream(done <-chan struct{}) <-chan int { 38 | s := make(chan int) 39 | values := []int{1, 2, 3, 4, 5} 40 | go func() { 41 | defer close(s) 42 | 43 | for _, v := range values { 44 | select { 45 | case <-done: 46 | return 47 | case s <- v: 48 | } 49 | } 50 | 51 | }() 52 | return s 53 | } 54 | 55 | func main() { 56 | in := asStream(nil) 57 | 58 | // map op: time 10 59 | mapFn := func(v int) int { 60 | return v * 10 61 | } 62 | 63 | // reduce op: sum 64 | reduceFn := func(r, v int) int { 65 | return r + v 66 | } 67 | 68 | sum := reduce(mapChan(in, mapFn), reduceFn) 69 | fmt.Println(sum) 70 | } 71 | -------------------------------------------------------------------------------- /4.distributed/locker/locker.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | 9 | clientv3 "go.etcd.io/etcd/client/v3" 10 | "go.etcd.io/etcd/client/v3/concurrency" 11 | ) 12 | 13 | func main() { 14 | rand.Seed(time.Now().UnixNano()) 15 | 16 | endpoints := []string{"http://127.0.0.1:2379"} 17 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | defer cli.Close() 22 | 23 | lockName := "my-lock" 24 | 25 | var wg sync.WaitGroup 26 | wg.Add(10) 27 | 28 | for i := 0; i < 10; i++ { 29 | go startSession(i, cli, lockName, &wg) 30 | } 31 | 32 | wg.Wait() 33 | } 34 | 35 | func startSession(id int, cli *clientv3.Client, lockName string, wg *sync.WaitGroup) { 36 | defer wg.Done() 37 | 38 | // 为锁生成session 39 | s1, err := concurrency.NewSession(cli) 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | defer s1.Close() 44 | locker := concurrency.NewLocker(s1, lockName) 45 | 46 | // 请求锁 47 | log.Println("acquiring lock for ID:", id) 48 | locker.Lock() 49 | log.Println("acquired lock for ID:", id) 50 | 51 | time.Sleep(time.Duration(rand.Intn(10)) * time.Second) 52 | locker.Unlock() 53 | 54 | log.Println("released lock for ID:", id) 55 | } 56 | -------------------------------------------------------------------------------- /11.classical_problems/barbershop_problem0/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "os" 7 | "os/signal" 8 | "sync" 9 | "syscall" 10 | "time" 11 | ) 12 | 13 | var ( 14 | seatsLock sync.Mutex 15 | seats int 16 | 17 | cond = sync.NewCond(&seatsLock) 18 | ) 19 | 20 | func main() { 21 | go barber() 22 | go customers() 23 | 24 | sigs := make(chan os.Signal, 1) 25 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 26 | <-sigs 27 | } 28 | 29 | func randomPause(max int) { 30 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(max))) 31 | } 32 | 33 | // 理发师 34 | func barber() { 35 | for { 36 | // 等待一个用户 37 | log.Println("Tony老师尝试请求一个顾客") 38 | seatsLock.Lock() 39 | for seats == 0 { 40 | cond.Wait() 41 | } 42 | seats-- 43 | seatsLock.Unlock() 44 | log.Println("Tony老师找到一位顾客,开始理发") 45 | 46 | randomPause(2000) 47 | } 48 | } 49 | 50 | // 模拟顾客陆陆续续的过来 51 | func customers() { 52 | for { 53 | randomPause(1000) 54 | go customer() 55 | } 56 | } 57 | 58 | // 顾客 59 | func customer() { 60 | seatsLock.Lock() 61 | defer seatsLock.Unlock() 62 | 63 | if seats == 3 { 64 | log.Println("没有空闲座位了,一位顾客离开了") 65 | return 66 | } 67 | seats++ 68 | cond.Broadcast() 69 | 70 | log.Println("一位顾客开始坐下排队理发") 71 | } 72 | -------------------------------------------------------------------------------- /7.orchestration/water2/water_test.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | import ( 4 | "math/rand" 5 | "sort" 6 | "sync" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestWaterFactory(t *testing.T) { 12 | var ch chan string 13 | 14 | releaseHydrogen := func() { 15 | ch <- "H" 16 | } 17 | 18 | releaseOxygen := func() { 19 | ch <- "O" 20 | } 21 | 22 | var N = 100 23 | ch = make(chan string, N*3) 24 | 25 | h2o := New() 26 | var wg sync.WaitGroup 27 | wg.Add(N * 3) 28 | // H 29 | for i := 0; i < 2*N; i++ { 30 | go func() { 31 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 32 | h2o.hydrogen(releaseHydrogen) 33 | wg.Done() 34 | }() 35 | } 36 | // O 37 | for i := 0; i < N; i++ { 38 | go func() { 39 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 40 | h2o.oxygen(releaseOxygen) 41 | wg.Done() 42 | }() 43 | } 44 | 45 | wg.Wait() 46 | 47 | if len(ch) != N*3 { 48 | t.Fatalf("expect %d atom but got %d", N*3, len(ch)) 49 | } 50 | 51 | var s = make([]string, 3) 52 | for i := 0; i < N; i++ { 53 | s[0] = <-ch 54 | s[1] = <-ch 55 | s[2] = <-ch 56 | sort.Strings(s) 57 | 58 | water := s[0] + s[1] + s[2] 59 | if water != "HHO" { 60 | t.Fatalf("expect a water molecule but got %s", water) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /5.channel/or_channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // refer to https://github.com/kat-co/concurrency-in-go-src/blob/master/concurrency-patterns-in-go/the-or-channel/fig-or-channel.go 9 | func or(channels ...<-chan any) <-chan any { 10 | switch len(channels) { 11 | case 0: 12 | return nil 13 | case 1: 14 | return channels[0] 15 | } 16 | 17 | orDone := make(chan any) 18 | go func() { 19 | defer close(orDone) 20 | 21 | switch len(channels) { 22 | case 2: 23 | select { 24 | case <-channels[0]: 25 | case <-channels[1]: 26 | } 27 | default: 28 | select { 29 | case <-channels[0]: 30 | case <-channels[1]: 31 | case <-channels[2]: 32 | case <-or(append(channels[3:], orDone)...): 33 | } 34 | } 35 | }() 36 | return orDone 37 | } 38 | 39 | func sig(after time.Duration) <-chan any { 40 | c := make(chan any) 41 | go func() { 42 | defer close(c) 43 | time.Sleep(after) 44 | }() 45 | return c 46 | } 47 | 48 | func main() { 49 | 50 | start := time.Now() 51 | 52 | <-or( 53 | sig(10*time.Second), 54 | sig(20*time.Second), 55 | sig(30*time.Second), 56 | sig(40*time.Second), 57 | sig(50*time.Second), 58 | sig(01*time.Minute), 59 | ) 60 | 61 | fmt.Printf("done after %v", time.Since(start)) 62 | } 63 | -------------------------------------------------------------------------------- /1.basic/rwmutex/hack/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | "time" 8 | "unsafe" 9 | ) 10 | 11 | const ( 12 | mutexLocked = 1 << iota // mutex is locked 13 | mutexWoken 14 | mutexStarving 15 | mutexWaiterShift = iota 16 | ) 17 | 18 | type RWMutex struct { 19 | sync.RWMutex 20 | } 21 | 22 | type m struct { 23 | w sync.Mutex 24 | writerSem uint32 25 | readerSem uint32 26 | readerCount int32 27 | readerWait int32 28 | } 29 | 30 | func (rw *RWMutex) ReaderCount() int { 31 | v := (*m)(unsafe.Pointer(&rw.RWMutex)) 32 | c := int(v.readerCount) 33 | if c < 0 { 34 | c = int(v.readerWait) 35 | } 36 | 37 | return c 38 | } 39 | 40 | func (rw *RWMutex) WriterCount() int { 41 | v := atomic.LoadInt32((*int32)(unsafe.Pointer(&rw.RWMutex))) 42 | v = v >> mutexWaiterShift 43 | v = v + (v & mutexLocked) 44 | return int(v) 45 | } 46 | 47 | func main() { 48 | var mu RWMutex 49 | 50 | for i := 0; i < 100; i++ { 51 | go func() { 52 | mu.RLock() 53 | time.Sleep(time.Hour) 54 | mu.RUnlock() 55 | }() 56 | } 57 | 58 | for i := 0; i < 50; i++ { 59 | go func() { 60 | mu.Lock() 61 | time.Sleep(time.Hour) 62 | mu.Unlock() 63 | }() 64 | } 65 | 66 | fmt.Println("readers: ", mu.ReaderCount()) 67 | fmt.Println("writer: ", mu.WriterCount()) 68 | } 69 | -------------------------------------------------------------------------------- /3.atomic/lock-free/value_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | "testing" 7 | ) 8 | 9 | // https://texlution.com/post/golang-lock-free-values-with-atomic-value/ 10 | 11 | type Config struct { 12 | sync.RWMutex 13 | endpoint string 14 | } 15 | 16 | func BenchmarkMutexSet(b *testing.B) { 17 | config := Config{} 18 | b.ReportAllocs() 19 | b.RunParallel(func(pb *testing.PB) { 20 | for pb.Next() { 21 | config.Lock() 22 | config.endpoint = "golang.org" 23 | config.Unlock() 24 | } 25 | }) 26 | } 27 | 28 | func BenchmarkMutexGet(b *testing.B) { 29 | config := Config{endpoint: "golang.org"} 30 | b.ReportAllocs() 31 | b.RunParallel(func(pb *testing.PB) { 32 | for pb.Next() { 33 | config.RLock() 34 | _ = config.endpoint 35 | config.RUnlock() 36 | } 37 | }) 38 | } 39 | 40 | func BenchmarkAtomicSet(b *testing.B) { 41 | var config atomic.Value 42 | c := Config{endpoint: "golang.org"} 43 | b.ReportAllocs() 44 | b.RunParallel(func(pb *testing.PB) { 45 | for pb.Next() { 46 | config.Store(c) 47 | } 48 | }) 49 | } 50 | 51 | func BenchmarkAtomicGet(b *testing.B) { 52 | var config atomic.Value 53 | config.Store(Config{endpoint: "golang.org"}) 54 | b.ReportAllocs() 55 | b.RunParallel(func(pb *testing.PB) { 56 | for pb.Next() { 57 | _ = config.Load().(Config) 58 | } 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /1.basic/time/rand_ticker/rand_ticker.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | time.Now().String() 11 | 12 | t := NewRandTicker(10*time.Second, 2*time.Second) 13 | defer t.Stop() 14 | 15 | for i := 0; i < 10; i++ { 16 | fired := <-t.C 17 | log.Println(fired.Format("fired at 15:04:05")) 18 | } 19 | } 20 | 21 | type RandTicker struct { 22 | C <-chan time.Time 23 | done chan struct{} 24 | } 25 | 26 | func NewRandTicker(d, variance time.Duration) *RandTicker { 27 | c := make(chan time.Time, 1) 28 | done := make(chan struct{}) 29 | 30 | go start(d, variance, c, done) 31 | 32 | return &RandTicker{ 33 | C: c, 34 | done: done, 35 | } 36 | } 37 | 38 | func start(d, variance time.Duration, c chan time.Time, done chan struct{}) { 39 | rnd := rand.New(rand.NewSource(time.Now().UnixNano())) 40 | for { 41 | vr := time.Duration(rnd.Int63n(int64(2*variance)) - int64(variance)) 42 | tmr := time.NewTimer(d + vr) // ±variance 43 | select { 44 | case firedAt := <-tmr.C: 45 | select { 46 | case c <- firedAt: 47 | default: 48 | } 49 | case <-done: 50 | tmr.Stop() 51 | //close(c) // Ticker in std does not close the channel. We keep consistent with it. 52 | return 53 | } 54 | } 55 | 56 | } 57 | 58 | func (rtk *RandTicker) Stop() { 59 | close(rtk.done) 60 | } 61 | -------------------------------------------------------------------------------- /11.classical_problems/barbershop_problem1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "os" 7 | "os/signal" 8 | "syscall" 9 | "time" 10 | ) 11 | 12 | type Semaphore chan struct{} 13 | 14 | func (s Semaphore) Acquire() { 15 | s <- struct{}{} 16 | } 17 | 18 | func (s Semaphore) TryAcquire() bool { 19 | select { 20 | case s <- struct{}{}: // 还有空位子 21 | return true 22 | default: // 没有空位子了,离开 23 | return false 24 | } 25 | } 26 | 27 | func (s Semaphore) Release() { 28 | <-s 29 | } 30 | 31 | var seats = make(Semaphore, 3) 32 | 33 | func main() { 34 | go barber() 35 | go customers() 36 | 37 | sigs := make(chan os.Signal, 1) 38 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 39 | <-sigs 40 | } 41 | 42 | func randomPause(max int) { 43 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(max))) 44 | } 45 | 46 | // 理发师 47 | func barber() { 48 | for { 49 | // 等待一个用户 50 | log.Println("Tony老师尝试请求一个顾客") 51 | seats.Release() 52 | log.Println("Tony老师找到一位顾客,开始理发") 53 | 54 | randomPause(2000) 55 | } 56 | } 57 | 58 | // 模拟顾客陆陆续续的过来 59 | func customers() { 60 | for { 61 | randomPause(1000) 62 | 63 | go customer() 64 | } 65 | } 66 | 67 | // 顾客 68 | func customer() { 69 | if ok := seats.TryAcquire(); ok { 70 | log.Println("一位顾客开始坐下排队理发") 71 | } else { 72 | log.Println("没有空闲座位了,一位顾客离开了") 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /4.distributed/mutex/mutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "math/rand" 7 | "sync" 8 | "time" 9 | 10 | clientv3 "go.etcd.io/etcd/client/v3" 11 | "go.etcd.io/etcd/client/v3/concurrency" 12 | ) 13 | 14 | func main() { 15 | rand.Seed(time.Now().UnixNano()) 16 | 17 | endpoints := []string{"http://127.0.0.1:2379"} 18 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | defer cli.Close() 23 | 24 | lockName := "my-lock" 25 | 26 | var wg sync.WaitGroup 27 | wg.Add(10) 28 | 29 | for i := 0; i < 10; i++ { 30 | go startSession(i, cli, lockName, &wg) 31 | } 32 | 33 | wg.Wait() 34 | } 35 | 36 | func startSession(id int, cli *clientv3.Client, lockName string, wg *sync.WaitGroup) { 37 | defer wg.Done() 38 | 39 | // 为锁生成session 40 | s1, err := concurrency.NewSession(cli) 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | defer s1.Close() 45 | m1 := concurrency.NewMutex(s1, lockName) 46 | 47 | // 请求锁 48 | log.Println("acquiring lock for ID:", id) 49 | if err := m1.Lock(context.TODO()); err != nil { 50 | log.Fatal(err) 51 | } 52 | log.Println("acquired lock for ID:", id) 53 | 54 | time.Sleep(time.Duration(rand.Intn(10)) * time.Second) 55 | 56 | if err := m1.Unlock(context.TODO()); err != nil { 57 | log.Fatal(err) 58 | } 59 | log.Println("released lock for ID:", id) 60 | } 61 | -------------------------------------------------------------------------------- /1.basic/wg/wg10/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "unsafe" 6 | ) 7 | 8 | type waitGroup1 struct { 9 | state1 [3]uint32 10 | } 11 | 12 | type waitGroup2 struct { 13 | state1 uint64 14 | sema uint32 15 | } 16 | 17 | type waitGroup3 struct { 18 | state1 uint64 19 | sema uint32 20 | } 21 | 22 | type A1 struct { 23 | a int32 24 | waitGroup1 25 | } 26 | 27 | type A2 struct { 28 | a int32 29 | waitGroup2 30 | } 31 | 32 | type A3 struct { 33 | a int32 34 | waitGroup3 35 | } 36 | 37 | func main() { 38 | var wg1 waitGroup1 39 | var a1 A1 40 | fmt.Println(unsafe.Alignof(wg1.state1)) 41 | fmt.Println(unsafe.Pointer(&wg1.state1)) 42 | fmt.Println(uintptr(unsafe.Pointer(&wg1.state1))%8 == 0) 43 | fmt.Println(uintptr(unsafe.Pointer(&a1.waitGroup1.state1))%8 == 0) 44 | fmt.Println() 45 | 46 | var wg2 waitGroup2 47 | var a2 A2 48 | fmt.Println(unsafe.Alignof(wg2.state1)) 49 | fmt.Println(unsafe.Pointer(&wg2.state1)) 50 | fmt.Println(uintptr(unsafe.Pointer(&wg2.state1))%8 == 0) 51 | fmt.Println(uintptr(unsafe.Pointer(&a2.waitGroup2.state1))%8 == 0) 52 | fmt.Println() 53 | 54 | var wg3 waitGroup3 55 | var a3 A3 56 | fmt.Println(unsafe.Alignof(wg3.state1)) 57 | fmt.Println(unsafe.Pointer(&wg3.state1)) 58 | fmt.Println(uintptr(unsafe.Pointer(&wg3.state1))%8 == 0) 59 | fmt.Println(uintptr(unsafe.Pointer(&a3.waitGroup3.state1))%8 == 0) 60 | } 61 | -------------------------------------------------------------------------------- /11.classical_problems/barbershop_problem2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "os" 7 | "os/signal" 8 | "syscall" 9 | "time" 10 | ) 11 | 12 | type Semaphore chan struct{} 13 | 14 | func (s Semaphore) Acquire() { 15 | s <- struct{}{} 16 | } 17 | 18 | func (s Semaphore) TryAcquire() bool { 19 | select { 20 | case s <- struct{}{}: // 还有空位子 21 | return true 22 | default: // 没有空位子了,离开 23 | return false 24 | } 25 | } 26 | 27 | func (s Semaphore) Release() { 28 | <-s 29 | } 30 | 31 | var seats = make(Semaphore, 10) 32 | 33 | func main() { 34 | // 托尼、凯文、艾伦理发师三巨头 35 | go barber("Tony") 36 | go barber("Kevin") 37 | go barber("Allen") 38 | 39 | go customers() 40 | 41 | sigs := make(chan os.Signal, 1) 42 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 43 | <-sigs 44 | } 45 | 46 | func randomPause(max int) { 47 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(max))) 48 | } 49 | 50 | // 理发师 51 | func barber(name string) { 52 | for { 53 | // 等待一个用户 54 | log.Println(name + "老师尝试请求一个顾客") 55 | seats.Release() 56 | log.Println(name + "老师找到一位顾客,开始理发") 57 | 58 | randomPause(2000) 59 | } 60 | } 61 | 62 | // 模拟顾客陆陆续续的过来 63 | func customers() { 64 | for { 65 | randomPause(1000) 66 | 67 | go customer() 68 | } 69 | } 70 | 71 | // 顾客 72 | func customer() { 73 | if ok := seats.TryAcquire(); ok { 74 | log.Println("一位顾客开始坐下排队理发") 75 | } else { 76 | log.Println("没有空闲座位了,一位顾客离开了") 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /4.distributed/queue/queue.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math/rand" 7 | "sync" 8 | "time" 9 | 10 | clientv3 "go.etcd.io/etcd/client/v3" 11 | recipe "go.etcd.io/etcd/client/v3/experimental/recipes" 12 | ) 13 | 14 | // Queue implements a multi-reader, multi-writer distributed queue. 15 | func main() { 16 | rand.Seed(time.Now().UnixNano()) 17 | 18 | endpoints := []string{"http://127.0.0.1:2379"} 19 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | defer cli.Close() 24 | 25 | queueName := "my-queue" 26 | 27 | var wg sync.WaitGroup 28 | wg.Add(20) 29 | 30 | for i := 0; i < 10; i++ { 31 | go write(i, cli, queueName, &wg) 32 | } 33 | 34 | for i := 0; i < 10; i++ { 35 | go read(10+i, cli, queueName, &wg) 36 | } 37 | 38 | wg.Wait() 39 | } 40 | 41 | func write(id int, cli *clientv3.Client, queueName string, wg *sync.WaitGroup) { 42 | defer wg.Done() 43 | 44 | q := recipe.NewQueue(cli, queueName) 45 | 46 | for i := 0; i < 10; i++ { 47 | q.Enqueue(fmt.Sprintf("g-%d-key-%d", id, i)) 48 | } 49 | } 50 | 51 | func read(id int, cli *clientv3.Client, queueName string, wg *sync.WaitGroup) { 52 | defer wg.Done() 53 | 54 | q := recipe.NewQueue(cli, queueName) 55 | 56 | for i := 0; i < 10; i++ { 57 | v, err := q.Dequeue() 58 | if err != nil { 59 | log.Fatal(err) 60 | } 61 | 62 | fmt.Printf("goroutine %d received: %s\n", id, v) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /5.channel/flat/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func orDone(done <-chan struct{}, c <-chan any) <-chan any { 8 | valStream := make(chan any) 9 | go func() { 10 | defer close(valStream) 11 | for { 12 | select { 13 | case <-done: 14 | return 15 | case v, ok := <-c: 16 | if ok == false { 17 | return 18 | } 19 | select { 20 | case valStream <- v: 21 | case <-done: 22 | } 23 | } 24 | } 25 | }() 26 | return valStream 27 | } 28 | func flat(done <-chan struct{}, chanStream <-chan <-chan any) <-chan any { 29 | valStream := make(chan any) 30 | go func() { 31 | defer close(valStream) 32 | for { 33 | var stream <-chan any 34 | select { 35 | case maybeStream, ok := <-chanStream: 36 | if ok == false { 37 | return 38 | } 39 | stream = maybeStream 40 | case <-done: 41 | return 42 | } 43 | for val := range orDone(done, stream) { 44 | select { 45 | case valStream <- val: 46 | case <-done: 47 | } 48 | } 49 | } 50 | }() 51 | return valStream 52 | } 53 | 54 | func main() { 55 | 56 | genVals := func() <-chan <-chan any { 57 | chanStream := make(chan (<-chan any)) 58 | go func() { 59 | defer close(chanStream) 60 | for i := 0; i < 10; i++ { 61 | stream := make(chan any, 1) 62 | stream <- i 63 | close(stream) 64 | chanStream <- stream 65 | } 66 | }() 67 | return chanStream 68 | } 69 | 70 | for v := range flat(nil, genVals()) { 71 | fmt.Printf("%v ", v) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /4.distributed/priority_queue/priority_queue.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math/rand" 7 | "sync" 8 | "time" 9 | 10 | clientv3 "go.etcd.io/etcd/client/v3" 11 | recipe "go.etcd.io/etcd/client/v3/experimental/recipes" 12 | ) 13 | 14 | // Queue implements a multi-reader, multi-writer distributed queue. 15 | func main() { 16 | rand.Seed(time.Now().UnixNano()) 17 | 18 | endpoints := []string{"http://127.0.0.1:2379"} 19 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | defer cli.Close() 24 | 25 | queueName := "my-queue" 26 | 27 | var wg sync.WaitGroup 28 | wg.Add(20) 29 | 30 | for i := 0; i < 10; i++ { 31 | go write(i, cli, queueName, &wg) 32 | } 33 | 34 | time.Sleep(time.Second) 35 | 36 | for i := 0; i < 10; i++ { 37 | go read(10+i, cli, queueName, &wg) 38 | } 39 | 40 | wg.Wait() 41 | } 42 | 43 | func write(id int, cli *clientv3.Client, queueName string, wg *sync.WaitGroup) { 44 | defer wg.Done() 45 | 46 | q := recipe.NewPriorityQueue(cli, queueName) 47 | 48 | for i := 0; i < 10; i++ { 49 | q.Enqueue(fmt.Sprintf("g-%d-key-%d", id, i), uint16(id*100+i)) 50 | } 51 | } 52 | 53 | func read(id int, cli *clientv3.Client, queueName string, wg *sync.WaitGroup) { 54 | defer wg.Done() 55 | 56 | q := recipe.NewPriorityQueue(cli, queueName) 57 | 58 | for i := 0; i < 10; i++ { 59 | v, err := q.Dequeue() 60 | if err != nil { 61 | log.Fatal(err) 62 | } 63 | 64 | fmt.Printf("goroutine %d received: %s\n", id, v) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /5.channel/perf/chan_test.go: -------------------------------------------------------------------------------- 1 | package chans 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | ) 7 | 8 | func BenchmarkChan_WithTwoCases(b *testing.B) { 9 | ch := make(chan struct{}, 1000) 10 | done := make(chan struct{}) 11 | go func() { 12 | for { 13 | select { 14 | case <-done: 15 | return 16 | case <-ch: 17 | } 18 | } 19 | }() 20 | 21 | for i := 0; i < b.N; i++ { 22 | ch <- struct{}{} 23 | } 24 | close(done) 25 | } 26 | 27 | func BenchmarkChan_WithOneCase(b *testing.B) { 28 | ch := make(chan struct{}, 1000) 29 | go func() { 30 | for { 31 | select { 32 | case <-ch: 33 | } 34 | } 35 | }() 36 | 37 | for i := 0; i < b.N; i++ { 38 | ch <- struct{}{} 39 | } 40 | } 41 | 42 | func BenchmarkChan_WithContext(b *testing.B) { 43 | ch := make(chan struct{}, 1000) 44 | ctx, cancel := context.WithCancel(context.Background()) 45 | go func() { 46 | done := ctx.Done() 47 | for { 48 | select { 49 | case <-done: 50 | case <-ch: 51 | } 52 | } 53 | }() 54 | 55 | for i := 0; i < b.N; i++ { 56 | ch <- struct{}{} 57 | } 58 | 59 | cancel() 60 | } 61 | 62 | func BenchmarkChan_Range(b *testing.B) { 63 | ch := make(chan struct{}, 1000) 64 | go func() { 65 | for range ch { 66 | } 67 | }() 68 | 69 | for i := 0; i < b.N; i++ { 70 | ch <- struct{}{} 71 | } 72 | } 73 | 74 | func BenchmarkChan_WithoutCase(b *testing.B) { 75 | ch := make(chan struct{}, 1000) 76 | go func() { 77 | for { 78 | <-ch 79 | } 80 | }() 81 | 82 | for i := 0; i < b.N; i++ { 83 | ch <- struct{}{} 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /7.orchestration/water/water_test.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | import ( 4 | "math/rand" 5 | "sort" 6 | "sync" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestWaterFactory(t *testing.T) { 12 | var ch chan string 13 | 14 | releaseHydrogen1 := func() { 15 | ch <- "H" 16 | } 17 | 18 | releaseHydrogen2 := func() { 19 | ch <- "h" 20 | } 21 | 22 | releaseOxygen := func() { 23 | ch <- "O" 24 | } 25 | 26 | var N = 100 27 | ch = make(chan string, N*3) 28 | 29 | h2o := New() 30 | var wg sync.WaitGroup 31 | wg.Add(N * 3) 32 | // h1 33 | go func() { 34 | for i := 0; i < N; i++ { 35 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 36 | h2o.hydrogen(releaseHydrogen1) 37 | wg.Done() 38 | } 39 | }() 40 | 41 | // h2 42 | go func() { 43 | for i := 0; i < N; i++ { 44 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 45 | h2o.hydrogen(releaseHydrogen2) 46 | wg.Done() 47 | } 48 | }() 49 | 50 | // o 51 | go func() { 52 | for i := 0; i < N; i++ { 53 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 54 | h2o.oxygen(releaseOxygen) 55 | wg.Done() 56 | } 57 | }() 58 | 59 | wg.Wait() 60 | 61 | if len(ch) != N*3 { 62 | t.Fatalf("expect %d atom but got %d", N*3, len(ch)) 63 | } 64 | 65 | var s = make([]string, 3) 66 | for i := 0; i < N; i++ { 67 | s[0] = <-ch 68 | s[1] = <-ch 69 | s[2] = <-ch 70 | sort.Strings(s) 71 | 72 | water := s[0] + s[1] + s[2] 73 | if water != "HOh" { 74 | t.Fatalf("expect a water molecule but got %s", water) 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /7.orchestration/water3/water_test.go: -------------------------------------------------------------------------------- 1 | package water 2 | 3 | import ( 4 | "math/rand" 5 | "sort" 6 | "sync" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestWaterFactory(t *testing.T) { 12 | var ch chan string 13 | 14 | releaseHydrogen1 := func() { 15 | ch <- "H" 16 | } 17 | 18 | releaseHydrogen2 := func() { 19 | ch <- "H" 20 | } 21 | 22 | releaseOxygen := func() { 23 | ch <- "O" 24 | } 25 | 26 | var N = 100 27 | ch = make(chan string, N*3) 28 | 29 | h2o := New() 30 | var wg sync.WaitGroup 31 | wg.Add(N * 3) 32 | // h1 33 | go func() { 34 | for i := 0; i < N; i++ { 35 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 36 | h2o.hydrogen(releaseHydrogen1) 37 | wg.Done() 38 | } 39 | }() 40 | 41 | // h2 42 | go func() { 43 | for i := 0; i < N; i++ { 44 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 45 | h2o.hydrogen(releaseHydrogen2) 46 | wg.Done() 47 | } 48 | }() 49 | 50 | // o 51 | go func() { 52 | for i := 0; i < N; i++ { 53 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 54 | h2o.oxygen(releaseOxygen) 55 | wg.Done() 56 | } 57 | }() 58 | 59 | wg.Wait() 60 | 61 | if len(ch) != N*3 { 62 | t.Fatalf("expect %d atom but got %d", N*3, len(ch)) 63 | } 64 | 65 | var s = make([]string, 3) 66 | for i := 0; i < N; i++ { 67 | s[0] = <-ch 68 | s[1] = <-ch 69 | s[2] = <-ch 70 | sort.Strings(s) 71 | 72 | water := s[0] + s[1] + s[2] 73 | if water != "HHO" { 74 | t.Fatalf("expect a water molecule but got %s", water) 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /10.ratelimit/uber/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "go.uber.org/ratelimit" 8 | ) 9 | 10 | func main() { 11 | // limit() 12 | // fmt.Println() 13 | 14 | // unlimit() 15 | // fmt.Println() 16 | 17 | slack() 18 | fmt.Println() 19 | 20 | withoutSlack() 21 | fmt.Println() 22 | } 23 | 24 | func limit() { 25 | fmt.Println("run at 100/s") 26 | rl := ratelimit.New(100) // 每秒100个,均匀漏出 27 | 28 | takenPrev := time.Now() 29 | for i := 0; i < 10; i++ { 30 | start := time.Now() 31 | rl.Take() 32 | fmt.Printf("#%d, interval: %v, took: %v\n", i, time.Since(takenPrev), time.Since(start)) 33 | takenPrev = time.Now() 34 | } 35 | } 36 | 37 | func unlimit() { 38 | fmt.Println("run as unlimited") 39 | rl := ratelimit.NewUnlimited() 40 | 41 | takenPrev := time.Now() 42 | for i := 0; i < 10; i++ { 43 | start := time.Now() 44 | rl.Take() 45 | fmt.Printf("#%d, interval: %v, took: %v\n", i, time.Since(takenPrev), time.Since(start)) 46 | takenPrev = time.Now() 47 | } 48 | } 49 | 50 | func slack() { 51 | fmt.Println("run with slack") 52 | rl := ratelimit.New(10) 53 | rl.Take() 54 | time.Sleep(160 * time.Millisecond) 55 | rl.Take() 56 | time.Sleep(20 * time.Millisecond) 57 | start := time.Now() 58 | rl.Take() 59 | fmt.Printf("took: %v\n", time.Since(start)) 60 | } 61 | 62 | func withoutSlack() { 63 | fmt.Println("run without slack") 64 | rl := ratelimit.New(10, ratelimit.WithoutSlack) 65 | rl.Take() 66 | time.Sleep(160 * time.Millisecond) 67 | rl.Take() 68 | time.Sleep(20 * time.Millisecond) 69 | start := time.Now() 70 | rl.Take() 71 | fmt.Printf("took: %v\n", time.Since(start)) 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 深入Go并发编程研讨课 2 | 3 | 企业级的并发库: [gofer](https://github.com/smallnest/gofer) 4 | 5 | Go提供了我们便利的进行并发编程的工具、方法和同步原语,同时也提供给我们诸多的犯错的机会,也就是俗称的“坑”。即使是顶级Go开发的项目,比如Docker、Kubernetes、gRPC、etcd, 都是有经验丰富的Go开发专家所开发,也踩过不少的并发的坑,而且依然源源不断的继续踩着,即便是标准库也是这样。(参考 []()) 6 | 7 | 分析和总结并发编程中的陷阱,避免重复踩在别人的坑中,正式本次培训课的重要内容。只有深入了解并发原语的实现,全面了解它们的特性和限制场景,注意它们的局限和容易踩的坑,才能提高我们的并发编程的能力。通过了解和学习其他人的经验和贡献的项目和库,我们可以更好的扩展我们的视野,避免重复的造轮子,或者说我们可以制作更好的轮子。 8 | 9 | 语言的内存模型定义了对变量的读写的可见性,可以清晰而准确读写事件的`happen before`关系。对于我们,可以很好地分析和编排goroutine的运行,避免数据的竞争和不一致的问题。 10 | 11 | 通过本次课程,你可以: 12 | 13 | - 了解基本同步原语的具体实现、hack同步原语进行扩展,了解它们的使用场景和坑,已经别人是怎么踩的 14 | - 了解一些扩展的同步源于,对于标准库sync包的补充 15 | - 对于规模很大的项目,分布式同步原语是必不可少的,带你了解便利的分布式同步原语 16 | - atomic可以保证对数据操作的一致性,利用CAS可以设计lock-free的数据结构 17 | - channel是Go语言进行并发编程的很好的工具,带你了解它的使用姿势 18 | - 了解Go语言的内存模型 19 | 20 | 21 | ## 并发原语在Go中的应用综述 22 | 23 | ## 基本并发原语 24 | - Mutex的实现、扩展功能和坑。 25 | - RWMutex的实现、扩展功能和坑。 26 | - Waitgroup的实现、坑 27 | - Cond的使用和坑 28 | - Once的实现和坑,单例的Eager/Lazy实现 29 | - Pool的坑, net.Conn的池 30 | - Map的实现、应用场景 31 | - Context的一些问题 32 | 33 | ## 扩展并发原语 34 | - 可重入锁 35 | - 信号量 36 | - SingleFlight及应用 37 | - ErrGroup 38 | - 自旋锁 39 | - 文件锁 40 | - 并发Map的多种实现 41 | 42 | ## 原子操作 43 | - 原子操作的实现 44 | - 操作的数据类型 45 | - 提供的函数 46 | - 通用Value类型 47 | - 扩展的原子操作库 48 | 49 | ## 分布式并发原语 50 | - 锁,Mutex, RWmutex实战 51 | - 栅栏 52 | - leader选举 53 | - 队列 54 | - STM 55 | - 其它分布式并发库 56 | 57 | ## channel 58 | - 常见易犯错的channel使用场景 59 | - 三大使用场景 60 | - Or-done模式的三种实现 61 | - 扇入 62 | - 扇出 63 | - Tee 64 | - Pipeline 65 | - 流式处理 66 | 67 | ## Go内存模型 68 | - init函数 69 | - goroutine 70 | - channel 71 | - Mutex/RWMutex 72 | - Waitgroup 73 | - Once 74 | - atomic 75 | 76 | ## 习题研讨 77 | 78 | [Go concurrency quizzes](https://github.com/smallnest/go-concurrent-quiz) -------------------------------------------------------------------------------- /1.basic/mutex/trylock/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | "time" 8 | "unsafe" 9 | ) 10 | 11 | const ( 12 | mutexLocked = 1 << iota // mutex is locked 13 | mutexWoken 14 | mutexStarving 15 | mutexWaiterShift = iota 16 | ) 17 | 18 | type Mutex struct { 19 | sync.Mutex 20 | } 21 | 22 | func (m *Mutex) TryLock() bool { 23 | return atomic.CompareAndSwapInt32((*int32)(unsafe.Pointer(&m.Mutex)), 0, mutexLocked) 24 | } 25 | 26 | func (m *Mutex) Count() int { 27 | v := atomic.LoadInt32((*int32)(unsafe.Pointer(&m.Mutex))) 28 | waiter := v >> mutexWaiterShift 29 | waiter = waiter + (v & mutexLocked) 30 | return int(waiter) 31 | } 32 | 33 | func (m *Mutex) IsLocked() bool { 34 | state := atomic.LoadInt32((*int32)(unsafe.Pointer(&m.Mutex))) 35 | return state&mutexLocked == mutexLocked 36 | } 37 | 38 | func (m *Mutex) IsWoken() bool { 39 | state := atomic.LoadInt32((*int32)(unsafe.Pointer(&m.Mutex))) 40 | return state&mutexWoken == mutexWoken 41 | } 42 | 43 | func (m *Mutex) IsStarving() bool { 44 | state := atomic.LoadInt32((*int32)(unsafe.Pointer(&m.Mutex))) 45 | return state&mutexStarving == mutexStarving 46 | } 47 | 48 | func main() { 49 | try() 50 | 51 | count() 52 | } 53 | 54 | func try() { 55 | var mu Mutex 56 | go func() { 57 | mu.Lock() 58 | time.Sleep(time.Second) 59 | mu.Unlock() 60 | }() 61 | 62 | time.Sleep(time.Second) 63 | 64 | ok := mu.TryLock() 65 | if ok { 66 | fmt.Println("got the lock") 67 | // do something 68 | mu.Unlock() 69 | return 70 | } 71 | 72 | fmt.Println("can't get the lock") 73 | } 74 | 75 | func count() { 76 | var mu Mutex 77 | for i := 0; i < 1000; i++ { 78 | go func() { 79 | mu.Lock() 80 | time.Sleep(time.Second) 81 | mu.Unlock() 82 | }() 83 | } 84 | 85 | time.Sleep(time.Second) 86 | 87 | fmt.Printf("waitings: %d, woken: %t, starving: %t\n", mu.Count(), mu.IsWoken(), mu.IsStarving()) 88 | } 89 | -------------------------------------------------------------------------------- /3.atomic/asm/asm.md: -------------------------------------------------------------------------------- 1 | ## 架构 2 | 3 | 查看: 4 | 5 | ```sh 6 | go tool dist list 7 | ``` 8 | 9 | 安装指定架构的标准库 10 | 11 | ``` 12 | ./buildpkg.sh $GOOS $GOARCH 13 | ``` 14 | 15 | 具体实现: https://github.com/golang/go/tree/master/src/runtime/internal/atomic 16 | 17 | ## darwin/amd64 18 | 19 | add: 20 | ```asm 21 | LOCK 22 | XADDQ CX, (AX) 23 | ``` 24 | 25 | load: 26 | ```asm 27 | MOVQ (AX), AX 28 | ```asm 29 | 30 | cas: 31 | ```asm 32 | LOCK 33 | CMPXCHGQ DX, (CX) 34 | ``` 35 | 36 | store: 37 | ```asm 38 | XCHGQ CX, (AX) 39 | ``` 40 | 41 | swap: 42 | ```asm 43 | XCHGQ CX, (AX) 44 | ``` 45 | 46 | 47 | ## windows/386 48 | 49 | add: 50 | ```asm 51 | sync/atomic.AddInt64(SB) 52 | ``` 53 | 54 | load: 55 | ```asm 56 | sync/atomic.LoadInt64(SB) 57 | ```asm 58 | 59 | cas: 60 | ```asm 61 | sync/atomic.CompareAndSwapInt64(SB) 62 | ``` 63 | 64 | store: 65 | ```asm 66 | sync/atomic.StoreInt64(SB) 67 | ``` 68 | 69 | swap: 70 | ```asm 71 | sync/atomic.SwapInt64(SB) 72 | ``` 73 | 74 | https://github.com/golang/go/blob/master/src/runtime/internal/atomic/asm_386.s 75 | 76 | ## windows arm 77 | 78 | add: 79 | ```asm 80 | sync/atomic.AddInt64(SB) 81 | ``` 82 | 83 | load: 84 | ```asm 85 | sync/atomic.LoadInt64(SB) 86 | ```asm 87 | 88 | cas: 89 | ```asm 90 | sync/atomic.CompareAndSwapInt64(SB) 91 | ``` 92 | 93 | store: 94 | ```asm 95 | sync/atomic.StoreInt64(SB) 96 | ``` 97 | 98 | swap: 99 | ```asm 100 | sync/atomic.SwapInt64(SB) 101 | ``` 102 | 103 | 104 | ## linux arm64 105 | `LDXR/STXR`、`LDAXR/STLXR` 106 | 107 | ## linux mips 108 | 109 | add: 110 | ```asm 111 | sync/atomic.AddInt64(SB) 112 | ``` 113 | 114 | load: 115 | ```asm 116 | sync/atomic.LoadInt64(SB) 117 | ```asm 118 | 119 | cas: 120 | ```asm 121 | sync/atomic.CompareAndSwapInt64(SB) 122 | ``` 123 | 124 | store: 125 | ```asm 126 | sync/atomic.StoreInt64(SB) 127 | ``` 128 | 129 | swap: 130 | ```asm 131 | sync/atomic.SwapInt64(SB) 132 | ``` 133 | 134 | ## linux mips64 135 | 136 | `SYNC` 137 | -------------------------------------------------------------------------------- /11.classical_problems/h2o_problem2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "math/rand" 7 | "sort" 8 | "time" 9 | 10 | "github.com/marusama/cyclicbarrier" 11 | ) 12 | 13 | // 有问题的例子 14 | 15 | // H2O 水分子工厂. 16 | type H2O struct { 17 | // 等待水分子的产生 18 | b cyclicbarrier.CyclicBarrier 19 | } 20 | 21 | // 创建一个水分子工厂. 22 | func New() *H2O { 23 | return &H2O{ 24 | b: cyclicbarrier.New(3), 25 | } 26 | } 27 | 28 | // 被氢原子goroutine调用,满足条件的时候就会提供一个H原子来产生一个水分子. 29 | func (h2o *H2O) hydrogen(releaseHydrogen func()) { 30 | // 准备一个H原子填坑 31 | releaseHydrogen() 32 | // 等待栅栏(另一个H原子和O原子的坑填好后栅栏开启) 33 | h2o.b.Await(context.Background()) 34 | } 35 | 36 | // 被氧原子goroutine调用,满足条件的时候就会一个O原子来产生一个水分子. 37 | func (h2o *H2O) oxygen(releaseOxygen func()) { 38 | releaseOxygen() 39 | // 等待栅栏(另两个H原子) 40 | h2o.b.Await(context.Background()) 41 | } 42 | 43 | func main() { 44 | var ch chan string 45 | releaseHydrogen := func() { 46 | ch <- "H" 47 | } 48 | releaseOxygen := func() { 49 | ch <- "O" 50 | } 51 | 52 | // goroutine数 53 | M := 2 54 | // 每个goroutine产生的原子数 55 | N := 10 56 | ch = make(chan string, M*N*3) 57 | h2o := New() 58 | 59 | for k := 0; k < M; k++ { 60 | go func() { 61 | for i := 0; i < N; i++ { 62 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 63 | h2o.hydrogen(releaseHydrogen) 64 | } 65 | }() 66 | go func() { 67 | for i := 0; i < N; i++ { 68 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 69 | h2o.hydrogen(releaseHydrogen) 70 | } 71 | }() 72 | go func() { 73 | for i := 0; i < N; i++ { 74 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 75 | h2o.oxygen(releaseOxygen) 76 | } 77 | }() 78 | } 79 | 80 | time.Sleep(5 * time.Second) 81 | 82 | s := make([]string, 3) 83 | for i := 0; i < len(ch)/3; i++ { 84 | s[0] = <-ch 85 | s[1] = <-ch 86 | s[2] = <-ch 87 | sort.Strings(s) 88 | water := s[0] + s[1] + s[2] 89 | if water != "HHO" { 90 | log.Panicf("expect a water molecule but got %s", water) 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /9.group/errgroup4/md5all.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "crypto/md5" 6 | "fmt" 7 | "io/ioutil" 8 | "log" 9 | "os" 10 | "path/filepath" 11 | 12 | "golang.org/x/sync/errgroup" 13 | ) 14 | 15 | // 一个多阶段的pipeline.使用有限的goroutine计算每个文件的md5值. 16 | func main() { 17 | m, err := MD5All(context.Background(), ".") 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | 22 | for k, sum := range m { 23 | fmt.Printf("%s:\t%x\n", k, sum) 24 | } 25 | } 26 | 27 | type result struct { 28 | path string 29 | sum [md5.Size]byte 30 | } 31 | 32 | // 遍历根目录下所有的文件和子文件夹,计算它们的md5的值. 33 | func MD5All(ctx context.Context, root string) (map[string][md5.Size]byte, error) { 34 | g, ctx := errgroup.WithContext(ctx) 35 | paths := make(chan string) // 文件路径channel 36 | 37 | g.Go(func() error { 38 | defer close(paths) 39 | return filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 40 | if err != nil { 41 | return err 42 | } 43 | if !info.Mode().IsRegular() { 44 | return nil 45 | } 46 | select { 47 | case paths <- path: 48 | case <-ctx.Done(): 49 | return ctx.Err() 50 | } 51 | return nil 52 | }) 53 | }) 54 | 55 | // 启动20个goroutine执行计算md5的任务,计算的文件由上一阶段的文件遍历子任务生成. 56 | c := make(chan result) 57 | const numDigesters = 20 58 | for i := 0; i < numDigesters; i++ { 59 | g.Go(func() error { 60 | for path := range paths { 61 | data, err := ioutil.ReadFile(path) 62 | if err != nil { 63 | return err 64 | } 65 | select { 66 | case c <- result{path, md5.Sum(data)}: 67 | case <-ctx.Done(): 68 | return ctx.Err() 69 | } 70 | } 71 | return nil 72 | }) 73 | } 74 | go func() { 75 | g.Wait() 76 | close(c) 77 | }() 78 | 79 | m := make(map[string][md5.Size]byte) 80 | for r := range c { // 将md5结果从chan中读取到map中 81 | m[r.path] = r.sum 82 | } 83 | 84 | // 等待所有的任务完成 85 | if err := g.Wait(); err != nil { 86 | return nil, err 87 | } 88 | return m, nil 89 | } 90 | -------------------------------------------------------------------------------- /4.distributed/stm/stm.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "math/rand" 8 | "sync" 9 | 10 | clientv3 "go.etcd.io/etcd/client/v3" 11 | "go.etcd.io/etcd/client/v3/concurrency" 12 | ) 13 | 14 | func main() { 15 | endpoints := []string{"http://127.0.0.1:2379"} 16 | 17 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | defer cli.Close() 22 | 23 | // set up "accounts" 24 | totalAccounts := 5 25 | for i := 0; i < totalAccounts; i++ { 26 | k := fmt.Sprintf("accts/%d", i) 27 | if _, err = cli.Put(context.TODO(), k, "100"); err != nil { 28 | log.Fatal(err) 29 | } 30 | } 31 | 32 | exchange := func(stm concurrency.STM) error { 33 | from, to := rand.Intn(totalAccounts), rand.Intn(totalAccounts) 34 | if from == to { 35 | // nothing to do 36 | return nil 37 | } 38 | // read values 39 | fromK, toK := fmt.Sprintf("accts/%d", from), fmt.Sprintf("accts/%d", to) 40 | fromV, toV := stm.Get(fromK), stm.Get(toK) 41 | fromInt, toInt := 0, 0 42 | fmt.Sscanf(fromV, "%d", &fromInt) 43 | fmt.Sscanf(toV, "%d", &toInt) 44 | 45 | // transfer amount 46 | xfer := fromInt / 2 47 | fromInt, toInt = fromInt-xfer, toInt+xfer 48 | 49 | // write back 50 | stm.Put(fromK, fmt.Sprintf("%d", fromInt)) 51 | stm.Put(toK, fmt.Sprintf("%d", toInt)) 52 | return nil 53 | } 54 | 55 | // concurrently exchange values between accounts 56 | var wg sync.WaitGroup 57 | wg.Add(10) 58 | for i := 0; i < 10; i++ { 59 | go func() { 60 | defer wg.Done() 61 | if _, serr := concurrency.NewSTM(cli, exchange); serr != nil { 62 | log.Fatal(serr) 63 | } 64 | }() 65 | } 66 | wg.Wait() 67 | 68 | // confirm account sum matches sum from beginning. 69 | sum := 0 70 | accts, err := cli.Get(context.TODO(), "accts/", clientv3.WithPrefix()) 71 | if err != nil { 72 | log.Fatal(err) 73 | } 74 | for _, kv := range accts.Kvs { 75 | v := 0 76 | fmt.Sscanf(string(kv.Value), "%d", &v) 77 | sum += v 78 | log.Printf("account %s: %d", kv.Key, v) 79 | } 80 | 81 | log.Println("account sum is", sum) 82 | } 83 | -------------------------------------------------------------------------------- /4.distributed/rwmutex/rwmutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | 9 | clientv3 "go.etcd.io/etcd/client/v3" 10 | "go.etcd.io/etcd/client/v3/concurrency" 11 | recipe "go.etcd.io/etcd/client/v3/experimental/recipes" 12 | ) 13 | 14 | func main() { 15 | rand.Seed(time.Now().UnixNano()) 16 | 17 | endpoints := []string{"http://127.0.0.1:2379"} 18 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | defer cli.Close() 23 | 24 | lockName := "my-lock" 25 | 26 | var wg sync.WaitGroup 27 | wg.Add(20) 28 | 29 | for i := 0; i < 10; i++ { 30 | go startLockSession(i, cli, lockName, &wg) 31 | } 32 | 33 | for i := 0; i < 10; i++ { 34 | go startRLockSession(10+i, cli, lockName, &wg) 35 | } 36 | 37 | wg.Wait() 38 | } 39 | 40 | func startLockSession(id int, cli *clientv3.Client, lockName string, wg *sync.WaitGroup) { 41 | defer wg.Done() 42 | 43 | // 为锁生成session 44 | s1, err := concurrency.NewSession(cli) 45 | if err != nil { 46 | log.Fatal(err) 47 | } 48 | defer s1.Close() 49 | m1 := recipe.NewRWMutex(s1, lockName) 50 | 51 | // 请求锁 52 | log.Println("acquiring lock for ID:", id) 53 | if err := m1.Lock(); err != nil { 54 | log.Fatal(err) 55 | } 56 | log.Println("acquired lock for ID:", id) 57 | 58 | time.Sleep(time.Duration(rand.Intn(10)) * time.Second) 59 | 60 | if err := m1.Unlock(); err != nil { 61 | log.Fatal(err) 62 | } 63 | log.Println("released lock for ID:", id) 64 | } 65 | 66 | func startRLockSession(id int, cli *clientv3.Client, lockName string, wg *sync.WaitGroup) { 67 | defer wg.Done() 68 | 69 | // 为锁生成session 70 | s1, err := concurrency.NewSession(cli) 71 | if err != nil { 72 | log.Fatal(err) 73 | } 74 | defer s1.Close() 75 | m1 := recipe.NewRWMutex(s1, lockName) 76 | 77 | // 请求锁 78 | log.Println("acquiring rlock for ID:", id) 79 | if err := m1.RLock(); err != nil { 80 | log.Fatal(err) 81 | } 82 | log.Println("acquired lock for ID:", id) 83 | 84 | time.Sleep(time.Duration(rand.Intn(10)) * time.Second) 85 | 86 | if err := m1.RUnlock(); err != nil { 87 | log.Fatal(err) 88 | } 89 | log.Println("released rlock for ID:", id) 90 | } 91 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/smallnest/dive-to-gosync-workshop 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/aaronjan/hunch v1.1.2 7 | github.com/dpaks/goworkers v1.8.0 8 | github.com/eapache/channels v1.1.0 9 | github.com/facebookgo/errgroup v0.0.0-20160209021148-779c8d7ef069 10 | github.com/fatih/color v1.10.0 11 | github.com/fatih/pool v3.0.0+incompatible 12 | github.com/gammazero/workerpool v1.1.2 13 | github.com/go-kratos/kratos v1.0.1 14 | github.com/go-pkgz/syncs v1.2.0 15 | github.com/ivpusic/grpool v1.0.0 16 | github.com/marusama/cyclicbarrier v1.1.0 17 | github.com/mdlayher/schedgroup v0.0.0-20200506182200-45678742bdc7 18 | github.com/petermattis/goid v0.0.0-20220111183729-e033e1e0bdb5 19 | github.com/reugn/async v0.3.1 20 | github.com/uw-labs/sync v0.0.0-20190307114256-1bb306bf6e71 21 | github.com/valyala/bytebufferpool v1.0.0 22 | go.etcd.io/etcd/client/v3 v3.5.1 23 | go.uber.org/atomic v1.7.0 24 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c 25 | ) 26 | 27 | require ( 28 | github.com/coreos/go-semver v0.3.0 // indirect 29 | github.com/coreos/go-systemd/v22 v22.3.2 // indirect 30 | github.com/eapache/queue v1.1.0 // indirect 31 | github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect 32 | github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect 33 | github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect 34 | github.com/gammazero/deque v0.1.0 // indirect 35 | github.com/gogo/protobuf v1.3.2 // indirect 36 | github.com/golang/protobuf v1.5.3 // indirect 37 | github.com/mattn/go-colorable v0.1.8 // indirect 38 | github.com/mattn/go-isatty v0.0.12 // indirect 39 | go.etcd.io/etcd/api/v3 v3.5.1 // indirect 40 | go.etcd.io/etcd/client/pkg/v3 v3.5.1 // indirect 41 | go.uber.org/multierr v1.6.0 // indirect 42 | go.uber.org/zap v1.17.0 // indirect 43 | golang.org/x/net v0.17.0 // indirect 44 | golang.org/x/sys v0.13.0 // indirect 45 | golang.org/x/text v0.13.0 // indirect 46 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect 47 | google.golang.org/grpc v1.56.3 // indirect 48 | google.golang.org/protobuf v1.33.0 // indirect 49 | ) 50 | 51 | replace github.com/AaronJan/Hunch => github.com/aaronjan/hunch v1.1.2 52 | -------------------------------------------------------------------------------- /1.basic/time/timer/reset.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | log.Println("✔︎ resetBeforeFired") 11 | resetBeforeFired() 12 | fmt.Println() 13 | 14 | log.Println("✘ wrongResetAfterFired") 15 | wrongResetAfterFired() 16 | fmt.Println() 17 | 18 | log.Println("✔︎ correctResetAfterFired") 19 | correctResetAfterFired() 20 | fmt.Println() 21 | 22 | log.Println("✔︎ stop n times") 23 | stopMore() 24 | fmt.Println() 25 | 26 | log.Println("✘ stop n times but with drain") 27 | wrongStopMore() 28 | fmt.Println() 29 | 30 | log.Println("✘ too many receiving") 31 | wrongReceiveMore() 32 | } 33 | 34 | func resetBeforeFired() { 35 | timer := time.NewTimer(5 * time.Second) 36 | b := timer.Stop() 37 | log.Printf("stop: %t", b) 38 | timer.Reset(10 * time.Second) 39 | t := <-timer.C 40 | log.Printf("fired at %s", t.String()) 41 | } 42 | 43 | func wrongResetAfterFired() { 44 | timer := time.NewTimer(5 * time.Millisecond) 45 | time.Sleep(time.Second) 46 | 47 | b := timer.Stop() 48 | log.Printf("stop: %t", b) 49 | timer.Reset(10 * time.Second) 50 | t := <-timer.C 51 | log.Printf("fired at %s", t.String()) 52 | } 53 | 54 | func correctResetAfterFired() { 55 | timer := time.NewTimer(5 * time.Millisecond) 56 | time.Sleep(time.Second) 57 | 58 | b := timer.Stop() 59 | log.Printf("stop: %t", b) 60 | if !b { 61 | <-timer.C 62 | } 63 | log.Printf("reset") 64 | timer.Reset(10 * time.Second) 65 | t := <-timer.C 66 | log.Printf("fired at %s", t.String()) 67 | } 68 | 69 | func wrongReceiveMore() { 70 | timer := time.NewTimer(5 * time.Millisecond) 71 | t := <-timer.C 72 | log.Printf("fired at %s", t.String()) 73 | 74 | t = <-timer.C 75 | log.Printf("receive again at %s", t.String()) 76 | } 77 | 78 | func stopMore() { 79 | timer := time.NewTimer(5 * time.Millisecond) 80 | b := timer.Stop() 81 | log.Printf("stop: %t", b) 82 | time.Sleep(time.Second) 83 | b = timer.Stop() 84 | log.Printf("stop more: %t", b) 85 | } 86 | 87 | func wrongStopMore() { 88 | timer := time.NewTimer(5 * time.Millisecond) 89 | b := timer.Stop() 90 | log.Printf("stop: %t", b) 91 | time.Sleep(time.Second) 92 | b = timer.Stop() 93 | if !b { 94 | <-timer.C 95 | } 96 | log.Printf("stop more: %t", b) 97 | } 98 | -------------------------------------------------------------------------------- /5.channel/fanout/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func fanOut(ch <-chan any, out []chan any, async bool) { 9 | go func() { 10 | defer func() { 11 | for i := 0; i < len(out); i++ { 12 | close(out[i]) 13 | } 14 | }() 15 | 16 | for v := range ch { 17 | v := v 18 | for i := 0; i < len(out); i++ { 19 | i := i 20 | if async { 21 | go func() { 22 | out[i] <- v 23 | }() 24 | } else { 25 | out[i] <- v 26 | } 27 | } 28 | } 29 | }() 30 | } 31 | 32 | func fanOutReflect(ch <-chan any, out []chan any) { 33 | go func() { 34 | defer func() { 35 | for i := 0; i < len(out); i++ { 36 | close(out[i]) 37 | } 38 | }() 39 | 40 | cases := make([]reflect.SelectCase, len(out)) 41 | for i := range cases { 42 | cases[i].Dir = reflect.SelectSend 43 | } 44 | 45 | for v := range ch { 46 | v := v 47 | for i := range cases { 48 | cases[i].Chan = reflect.ValueOf(out[i]) 49 | cases[i].Send = reflect.ValueOf(v) 50 | } 51 | 52 | for _ = range cases { // for each channel 53 | chosen, _, _ := reflect.Select(cases) 54 | cases[chosen].Chan = reflect.ValueOf(nil) 55 | } 56 | } 57 | }() 58 | } 59 | 60 | func asStream(done <-chan struct{}) <-chan any { 61 | s := make(chan any) 62 | values := []int{1, 2, 3, 4, 5} 63 | go func() { 64 | defer close(s) 65 | 66 | for _, v := range values { 67 | select { 68 | case <-done: 69 | return 70 | case s <- v: 71 | } 72 | } 73 | 74 | }() 75 | return s 76 | } 77 | 78 | func main() { 79 | source := asStream(nil) 80 | channels := make([]chan any, 5) 81 | 82 | fmt.Println("fanOut") 83 | for i := 0; i < 5; i++ { 84 | channels[i] = make(chan any) 85 | } 86 | fanOut(source, channels, false) 87 | for i := 0; i < 5; i++ { 88 | for j := 0; j < 5; j++ { 89 | fmt.Printf("channel#%d: %v\n", j, <-channels[j]) 90 | } 91 | } 92 | 93 | fmt.Println("fanOut By Reflect") 94 | source = asStream(nil) 95 | for i := 0; i < 5; i++ { 96 | channels[i] = make(chan any) 97 | } 98 | fanOutReflect(source, channels) 99 | for i := 0; i < 5; i++ { 100 | for j := 0; j < 5; j++ { 101 | fmt.Printf("channel#%d: %v\n", j, <-channels[j]) 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /11.classical_problems/dining_philosophers_problem0/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "net/http" 7 | _ "net/http/pprof" 8 | "os" 9 | "os/signal" 10 | "sync" 11 | "syscall" 12 | "time" 13 | 14 | "github.com/fatih/color" 15 | ) 16 | 17 | func init() { 18 | rand.Seed(time.Now().UTC().UnixNano()) 19 | } 20 | 21 | // Chopstick 代表筷子. 22 | type Chopstick struct{ sync.Mutex } 23 | 24 | // Philosopher 代表哲学家. 25 | type Philosopher struct { 26 | // 哲学家的名字 27 | name string 28 | // 左手一只和右手一只筷子 29 | leftChopstick, rightChopstick *Chopstick 30 | status string 31 | } 32 | 33 | // 无休止的进餐和冥想. 34 | // 吃完睡(冥想、打坐), 睡完吃. 35 | // 可以调整吃睡的时间来增加或者减少抢夺叉子的机会. 36 | func (p *Philosopher) dine() { 37 | for { 38 | mark(p, "冥想") 39 | randomPause(10) 40 | 41 | mark(p, "饿了") 42 | p.leftChopstick.Lock() // 先尝试拿起左手边的筷子 43 | mark(p, "拿起左手筷子") 44 | randomPause(100) 45 | p.rightChopstick.Lock() // 再尝试拿起右手边的筷子 46 | 47 | mark(p, "用膳") 48 | randomPause(10) 49 | 50 | p.rightChopstick.Unlock() // 先尝试放下右手边的筷子 51 | p.leftChopstick.Unlock() // 再尝试拿起左手边的筷子 52 | } 53 | } 54 | 55 | // 随机暂停一段时 56 | func randomPause(max int) { 57 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(max))) 58 | } 59 | 60 | // 显示此哲学家的状态 61 | func mark(p *Philosopher, action string) { 62 | fmt.Printf("%s开始%s\n", p.name, action) 63 | p.status = fmt.Sprintf("%s开始%s\n", p.name, action) 64 | } 65 | 66 | func main() { 67 | go http.ListenAndServe("localhost:8972", nil) 68 | 69 | // 哲学家数量 70 | count := 5 71 | 72 | // 创建5根筷子 73 | chopsticks := make([]*Chopstick, count) 74 | for i := 0; i < count; i++ { 75 | chopsticks[i] = new(Chopstick) 76 | } 77 | 78 | // 79 | names := []string{color.RedString("孔子"), color.MagentaString("庄子"), color.CyanString("墨子"), color.GreenString("孙子"), color.WhiteString("老子")} 80 | 81 | // 创建哲学家, 分配给他们左右手边的叉子,领他们做到圆餐桌上. 82 | philosophers := make([]*Philosopher, count) 83 | for i := 0; i < count; i++ { 84 | philosophers[i] = &Philosopher{ 85 | name: names[i], leftChopstick: chopsticks[i], rightChopstick: chopsticks[(i+1)%count], 86 | } 87 | go philosophers[i].dine() 88 | } 89 | 90 | sigs := make(chan os.Signal, 1) 91 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 92 | <-sigs 93 | 94 | fmt.Println("退出中... 每个哲学家的状态:") 95 | for _, p := range philosophers { 96 | fmt.Print(p.status) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /11.classical_problems/h2o_problem1_2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math/rand" 7 | "sort" 8 | "time" 9 | ) 10 | 11 | type Semaphore chan struct{} 12 | 13 | func (s Semaphore) Acquire(n int) { 14 | for i := 0; i < n; i++ { 15 | s <- struct{}{} 16 | } 17 | } 18 | 19 | func (s Semaphore) Release(n int) { 20 | for i := 0; i < n; i++ { 21 | <-s 22 | } 23 | } 24 | 25 | // H2O 水分子工厂. 26 | type H2O struct { 27 | // 氢原子的信号量 28 | semaH Semaphore 29 | // 氧原子的信号量 30 | semaO Semaphore 31 | } 32 | 33 | // 创建一个水分子工厂. 34 | func New() *H2O { 35 | return &H2O{ 36 | semaH: make(Semaphore, 2), 37 | semaO: make(Semaphore, 0), 38 | } 39 | } 40 | 41 | // 被氢原子goroutine调用,满足条件的时候就会提供一个H原子来产生一个水分子. 42 | func (h2o *H2O) hydrogen(releaseHydrogen func()) { 43 | h2o.semaH.Acquire(1) 44 | releaseHydrogen() 45 | 46 | h2o.semaO.Release(1) 47 | } 48 | 49 | // 被氧原子goroutine调用,满足条件的时候就会一个O原子来产生一个水分子. 50 | func (h2o *H2O) oxygen(releaseOxygen func()) { 51 | h2o.semaO.Acquire(2) 52 | 53 | releaseOxygen() 54 | 55 | h2o.semaH.Release(2) 56 | } 57 | 58 | func main() { 59 | var ch chan string 60 | releaseHydrogen := func() { 61 | ch <- "H" 62 | fmt.Print("H") 63 | } 64 | releaseOxygen := func() { 65 | ch <- "O" 66 | fmt.Print("O") 67 | } 68 | 69 | // goroutine数 70 | M := 2 71 | // 每个goroutine产生的原子数 72 | N := 10 73 | ch = make(chan string, M*N*3) 74 | h2o := New() 75 | 76 | for k := 0; k < M; k++ { 77 | go func() { 78 | for i := 0; i < N; i++ { 79 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 80 | h2o.hydrogen(releaseHydrogen) 81 | } 82 | }() 83 | go func() { 84 | for i := 0; i < N; i++ { 85 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 86 | h2o.hydrogen(releaseHydrogen) 87 | } 88 | }() 89 | go func() { 90 | for i := 0; i < N; i++ { 91 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 92 | h2o.oxygen(releaseOxygen) 93 | } 94 | }() 95 | } 96 | 97 | time.Sleep(10 * time.Second) 98 | 99 | s := make([]string, 3) 100 | for i := 0; i < len(ch)/3; i++ { 101 | s[0] = <-ch 102 | s[1] = <-ch 103 | s[2] = <-ch 104 | sort.Strings(s) 105 | water := s[0] + s[1] + s[2] 106 | if water != "HHO" { 107 | log.Panicf("expect a water molecule but got %s", water) 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /11.classical_problems/h2o_problem1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math/rand" 7 | "sort" 8 | "time" 9 | ) 10 | 11 | type Semaphore chan struct{} 12 | 13 | func (s Semaphore) Acquire(n int) { 14 | for i := 0; i < n; i++ { 15 | s <- struct{}{} 16 | } 17 | } 18 | 19 | func (s Semaphore) Release(n int) { 20 | for i := 0; i < n; i++ { 21 | <-s 22 | } 23 | } 24 | 25 | // H2O 水分子工厂. 26 | type H2O struct { 27 | // 氢原子的信号量 28 | semaH Semaphore 29 | // 氧原子的信号量 30 | semaO Semaphore 31 | } 32 | 33 | // 创建一个水分子工厂. 34 | func New() *H2O { 35 | return &H2O{ 36 | semaH: make(Semaphore, 2), 37 | semaO: make(Semaphore, 0), 38 | } 39 | } 40 | 41 | // 被氢原子goroutine调用,满足条件的时候就会提供一个H原子来产生一个水分子. 42 | func (h2o *H2O) hydrogen(releaseHydrogen func()) { 43 | h2o.semaO.Acquire(1) 44 | 45 | releaseHydrogen() 46 | 47 | h2o.semaH.Release(1) 48 | } 49 | 50 | // 被氧原子goroutine调用,满足条件的时候就会一个O原子来产生一个水分子. 51 | func (h2o *H2O) oxygen(releaseOxygen func()) { 52 | h2o.semaH.Acquire(2) 53 | 54 | releaseOxygen() 55 | 56 | h2o.semaO.Release(2) 57 | } 58 | 59 | func main() { 60 | var ch chan string 61 | releaseHydrogen := func() { 62 | ch <- "H" 63 | fmt.Print("H") 64 | } 65 | releaseOxygen := func() { 66 | ch <- "O" 67 | fmt.Print("O") 68 | } 69 | 70 | // goroutine数 71 | M := 2 72 | // 每个goroutine产生的原子数 73 | N := 10 74 | ch = make(chan string, M*N*3) 75 | h2o := New() 76 | 77 | for k := 0; k < M; k++ { 78 | go func() { 79 | for i := 0; i < N; i++ { 80 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 81 | h2o.hydrogen(releaseHydrogen) 82 | } 83 | }() 84 | go func() { 85 | for i := 0; i < N; i++ { 86 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 87 | h2o.hydrogen(releaseHydrogen) 88 | } 89 | }() 90 | go func() { 91 | for i := 0; i < N; i++ { 92 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 93 | h2o.oxygen(releaseOxygen) 94 | } 95 | }() 96 | } 97 | 98 | time.Sleep(10 * time.Second) 99 | 100 | s := make([]string, 3) 101 | for i := 0; i < len(ch)/3; i++ { 102 | s[0] = <-ch 103 | s[1] = <-ch 104 | s[2] = <-ch 105 | sort.Strings(s) 106 | water := s[0] + s[1] + s[2] 107 | if water != "HHO" { 108 | log.Panicf("expect a water molecule but got %s", water) 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /4.distributed/barrier/barrier.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | 9 | clientv3 "go.etcd.io/etcd/client/v3" 10 | "go.etcd.io/etcd/client/v3/concurrency" 11 | recipe "go.etcd.io/etcd/client/v3/experimental/recipes" 12 | ) 13 | 14 | func main() { 15 | rand.Seed(time.Now().UnixNano()) 16 | 17 | barrier() 18 | } 19 | 20 | func barrier() { 21 | endpoints := []string{"http://127.0.0.1:2379"} 22 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | defer cli.Close() 27 | 28 | barrierName := "my-test" 29 | 30 | b := recipe.NewBarrier(cli, barrierName) 31 | err = b.Hold() 32 | if err != nil { 33 | panic(err) 34 | } 35 | var wg sync.WaitGroup 36 | wg.Add(10) 37 | 38 | for i := 0; i < 10; i++ { 39 | i := i 40 | go func() { 41 | b := recipe.NewBarrier(cli, barrierName) 42 | 43 | time.Sleep(time.Duration(rand.Intn(10)) * time.Second) 44 | log.Println("enter for ID:", i) 45 | err := b.Wait() 46 | if err != nil { 47 | panic(err) 48 | } 49 | log.Println("entered for ID:", i) 50 | wg.Done() 51 | }() 52 | } 53 | 54 | time.Sleep(12 * time.Second) 55 | err = b.Release() 56 | if err != nil { 57 | panic(err) 58 | } 59 | 60 | wg.Wait() 61 | } 62 | 63 | func doubleBarrier() { 64 | endpoints := []string{"http://127.0.0.1:2379"} 65 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 66 | if err != nil { 67 | log.Fatal(err) 68 | } 69 | defer cli.Close() 70 | 71 | s, err := concurrency.NewSession(cli) 72 | if err != nil { 73 | log.Fatal(err) 74 | } 75 | defer s.Close() 76 | 77 | barrierName := "my-test" 78 | 79 | var wg sync.WaitGroup 80 | wg.Add(10) 81 | 82 | var leaveWG sync.WaitGroup 83 | leaveWG.Add(10) 84 | 85 | for i := 0; i < 10; i++ { 86 | i := i 87 | go func() { 88 | b := recipe.NewDoubleBarrier(s, barrierName, 10) 89 | 90 | time.Sleep(time.Duration(rand.Intn(10)) * time.Second) 91 | log.Println("enter for ID:", i) 92 | b.Enter() 93 | log.Println("entered for ID:", i) 94 | wg.Done() 95 | 96 | time.Sleep(time.Duration(rand.Intn(20)) * time.Second) 97 | log.Println("leave for ID:", i) 98 | b.Leave() 99 | log.Println("left for ID:", i) 100 | leaveWG.Done() 101 | }() 102 | } 103 | 104 | wg.Wait() 105 | 106 | leaveWG.Wait() 107 | } 108 | -------------------------------------------------------------------------------- /5.channel/fanout2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "time" 7 | ) 8 | 9 | func fanOut(ch <-chan any, out []chan any) { 10 | go func() { 11 | defer func() { 12 | for i := 0; i < len(out); i++ { 13 | close(out[i]) 14 | } 15 | }() 16 | 17 | // roundrobin 18 | var i = 0 19 | var n = len(out) 20 | for v := range ch { 21 | v := v 22 | out[i] <- v 23 | i = (i + 1) % n 24 | } 25 | }() 26 | } 27 | 28 | func fanOutReflect(ch <-chan any, out []chan any) { 29 | go func() { 30 | defer func() { 31 | for i := 0; i < len(out); i++ { 32 | close(out[i]) 33 | } 34 | }() 35 | 36 | cases := make([]reflect.SelectCase, len(out)) 37 | for i := range cases { 38 | cases[i].Dir = reflect.SelectSend 39 | cases[i].Chan = reflect.ValueOf(out[i]) 40 | 41 | } 42 | 43 | for v := range ch { 44 | v := v 45 | for i := range cases { 46 | cases[i].Send = reflect.ValueOf(v) 47 | } 48 | _, _, _ = reflect.Select(cases) 49 | } 50 | }() 51 | } 52 | 53 | func asStream(done <-chan struct{}) <-chan any { 54 | s := make(chan any) 55 | values := []int{1, 2, 3, 4, 5} 56 | go func() { 57 | defer close(s) 58 | 59 | for _, v := range values { 60 | select { 61 | case <-done: 62 | return 63 | case s <- v: 64 | } 65 | } 66 | 67 | }() 68 | return s 69 | } 70 | 71 | func main() { 72 | done := make(chan struct{}) 73 | source := asStream(done) 74 | channels := make([]chan any, 5) 75 | 76 | fmt.Println("fanOut") 77 | for i := 0; i < 5; i++ { 78 | channels[i] = make(chan any) 79 | } 80 | fanOut(source, channels) 81 | for i := 0; i < 5; i++ { 82 | i := i 83 | go func() { 84 | for j := 0; j < 5; j++ { 85 | v, ok := <-channels[i] 86 | if ok { 87 | fmt.Printf("channel#%d: %v\n", i, v) 88 | } 89 | 90 | } 91 | }() 92 | } 93 | time.Sleep(time.Second) 94 | close(done) 95 | 96 | fmt.Println("fanOut By Reflect") 97 | done = make(chan struct{}) 98 | source = asStream(done) 99 | for i := 0; i < 5; i++ { 100 | channels[i] = make(chan any) 101 | } 102 | fanOutReflect(source, channels) 103 | for i := 0; i < 5; i++ { 104 | i := i 105 | go func() { 106 | for j := 0; j < 5; j++ { 107 | v, ok := <-channels[i] 108 | if ok { 109 | fmt.Printf("channel#%d: %v\n", i, v) 110 | } 111 | } 112 | }() 113 | } 114 | time.Sleep(time.Second) 115 | close(done) 116 | 117 | } 118 | -------------------------------------------------------------------------------- /11.classical_problems/hilzers_barbershop_problem/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "os" 7 | "os/signal" 8 | "sync" 9 | "syscall" 10 | "time" 11 | ) 12 | 13 | // Semaphore定义 14 | type Semaphore chan struct{} 15 | 16 | func (s Semaphore) Acquire() { 17 | s <- struct{}{} 18 | } 19 | 20 | func (s Semaphore) TryAcquire() bool { 21 | select { 22 | case s <- struct{}{}: // 还有空位子 23 | return true 24 | default: // 没有空位子了,离开 25 | return false 26 | } 27 | } 28 | 29 | func (s Semaphore) Release() { 30 | <-s 31 | } 32 | 33 | // 顾客 34 | var ( 35 | // 控制顾客的总数 36 | customerMutex sync.Mutex 37 | customerMaxCount = 20 38 | customerCount = 0 39 | 40 | // 沙发的容量 41 | sofaSema Semaphore = make(chan struct{}, 4) 42 | ) 43 | 44 | // 收银台 45 | var ( 46 | // 同时只有一对理发师和顾客结账 47 | paySema Semaphore = make(chan struct{}, 1) 48 | // 顾客拿到发票才会离开,控制开票 49 | receiptSema Semaphore = make(chan struct{}, 1) 50 | ) 51 | 52 | // 理发师工作 53 | func barber(name string) { 54 | for { 55 | // 等待一个用户 56 | log.Println(name + "老师尝试请求一个顾客") 57 | sofaSema.Release() 58 | log.Println(name + "老师找到一位顾客,开始理发") 59 | 60 | randomPause(2000) 61 | 62 | log.Println(name + "老师理完发,等待顾客付款") 63 | paySema.Acquire() 64 | log.Println(name + "老师给付完款的顾客发票") 65 | receiptSema.Release() 66 | log.Println(name + "老师服务完一位顾客") 67 | 68 | } 69 | } 70 | 71 | // 模拟顾客陆陆续续的过来 72 | func customers() { 73 | for { 74 | randomPause(500) 75 | 76 | go customer() 77 | } 78 | } 79 | 80 | // 顾客 81 | func customer() { 82 | customerMutex.Lock() 83 | if customerCount == customerMaxCount { 84 | log.Println("没有空闲座位了,一位顾客离开了") 85 | customerMutex.Unlock() 86 | return 87 | } 88 | customerCount++ 89 | customerMutex.Unlock() 90 | 91 | log.Println("一位顾客开始等沙发坐下") 92 | sofaSema.Acquire() 93 | log.Println("一位顾客找到空闲沙发坐下,直到被理发师叫起理发") 94 | 95 | paySema.Release() 96 | log.Println("一位顾客已付完钱") 97 | receiptSema.Acquire() 98 | log.Println("一位顾客拿到发票,离开") 99 | 100 | customerMutex.Lock() 101 | customerCount-- 102 | customerMutex.Unlock() 103 | } 104 | 105 | func randomPause(max int) { 106 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(max))) 107 | } 108 | 109 | func main() { 110 | // 托尼、凯文、艾伦理发师三巨头 111 | go barber("Tony") 112 | go barber("Kevin") 113 | go barber("Allen") 114 | 115 | go customers() 116 | 117 | sigs := make(chan os.Signal, 1) 118 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 119 | <-sigs 120 | } 121 | -------------------------------------------------------------------------------- /10.ratelimit/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY= 5 | github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= 6 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 7 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 8 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 9 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 10 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 11 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 12 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 13 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 14 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 15 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 16 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 17 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 18 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 19 | go.uber.org/ratelimit v0.1.0 h1:U2AruXqeTb4Eh9sYQSTrMhH8Cb7M0Ian2ibBOnBcnAw= 20 | go.uber.org/ratelimit v0.1.0/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= 21 | golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= 22 | golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 23 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 24 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= 25 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 26 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 27 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 28 | -------------------------------------------------------------------------------- /11.classical_problems/dining_philosophers_problem4/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "net/http" 7 | _ "net/http/pprof" 8 | "os" 9 | "os/signal" 10 | "sync" 11 | "syscall" 12 | "time" 13 | 14 | "github.com/fatih/color" 15 | ) 16 | 17 | func init() { 18 | rand.Seed(time.Now().UTC().UnixNano()) 19 | } 20 | 21 | // Chopstick 代表筷子. 22 | type Chopstick struct{ sync.Mutex } 23 | 24 | // Philosopher 代表哲学家. 25 | type Philosopher struct { 26 | // 哲学家的名字 27 | name string 28 | // 左手一只和右手一只筷子 29 | leftChopstick, rightChopstick *Chopstick 30 | status string 31 | 32 | mu *sync.Mutex 33 | } 34 | 35 | // 无休止的进餐和冥想. 36 | // 吃完睡(冥想、打坐), 睡完吃. 37 | // 可以调整吃睡的时间来增加或者减少抢夺叉子的机会. 38 | func (p *Philosopher) dine() { 39 | for { 40 | mark(p, "冥想") 41 | randomPause(10) 42 | 43 | mark(p, "饿了") 44 | p.mu.Lock() 45 | p.leftChopstick.Lock() // 先尝试拿起左手边的筷子 46 | mark(p, "拿起左手筷子") 47 | p.rightChopstick.Lock() // 再尝试拿起右手边的筷子 48 | p.mu.Unlock() 49 | 50 | mark(p, "用膳") 51 | randomPause(10) 52 | 53 | p.rightChopstick.Unlock() // 先尝试放下右手边的筷子 54 | p.leftChopstick.Unlock() // 再尝试拿起左手边的筷子 55 | } 56 | } 57 | 58 | // 随机暂停一段时 59 | func randomPause(max int) { 60 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(max))) 61 | } 62 | 63 | // 显示此哲学家的状态 64 | func mark(p *Philosopher, action string) { 65 | fmt.Printf("%s开始%s\n", p.name, action) 66 | p.status = fmt.Sprintf("%s开始%s\n", p.name, action) 67 | } 68 | 69 | func main() { 70 | go http.ListenAndServe("localhost:8972", nil) 71 | 72 | // 哲学家数量 73 | count := 5 74 | 75 | // 创建5根筷子 76 | chopsticks := make([]*Chopstick, count) 77 | for i := 0; i < count; i++ { 78 | chopsticks[i] = new(Chopstick) 79 | } 80 | 81 | var mu sync.Mutex 82 | // 83 | names := []string{color.RedString("孔子"), color.MagentaString("庄子"), color.CyanString("墨子"), color.GreenString("孙子"), color.WhiteString("老子")} 84 | 85 | // 创建哲学家, 分配给他们左右手边的叉子,领他们做到圆餐桌上. 86 | philosophers := make([]*Philosopher, count) 87 | for i := 0; i < count; i++ { 88 | philosophers[i] = &Philosopher{ 89 | name: names[i], leftChopstick: chopsticks[i], rightChopstick: chopsticks[(i+1)%count], 90 | mu: &mu, 91 | } 92 | go philosophers[i].dine() 93 | } 94 | 95 | sigs := make(chan os.Signal, 1) 96 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 97 | <-sigs 98 | 99 | fmt.Println("退出中... 每个哲学家的状态:") 100 | for _, p := range philosophers { 101 | fmt.Print(p.status) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /1.basic/once/helper/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | // https://github.com/golang/go/issues/56102#issuecomment-1311857716 9 | // https://github.com/adg/sync/blob/main/once.go 10 | // https://github.com/carlmjohnson/syncx/blob/main/once.go 11 | 12 | func main() { 13 | pi := OnceValue(func() float64 { return 3.14 }) 14 | 15 | fmt.Println(pi()) 16 | fmt.Println(pi()) 17 | } 18 | 19 | // OnceFunc returns a function that invokes f only once. The returned function 20 | // may be called concurrently. 21 | // 22 | // If f panics, the returned function will panic with the same value on every call. 23 | func OnceFunc(f func()) func() { 24 | var once sync.Once 25 | var valid bool 26 | var p any 27 | return func() { 28 | once.Do(func() { 29 | defer func() { 30 | p = recover() 31 | if !valid { 32 | // Re-panic immediately so on the first call the user gets a 33 | // complete stack trace into f. 34 | panic(p) 35 | } 36 | }() 37 | f() 38 | valid = true // Set only if f does not panic 39 | }) 40 | if !valid { 41 | panic(p) 42 | } 43 | } 44 | } 45 | 46 | // OnceValue returns a function that invokes f only once and returns the value 47 | // returned by f. The returned function may be called concurrently. 48 | // 49 | // If f panics, the returned function will panic with the same value on every call. 50 | func OnceValue[T any](f func() T) func() T { 51 | var once sync.Once 52 | var valid bool 53 | var p any 54 | var result T 55 | return func() T { 56 | once.Do(func() { 57 | defer func() { 58 | p = recover() 59 | if !valid { 60 | panic(p) 61 | } 62 | }() 63 | result = f() 64 | valid = true 65 | }) 66 | if !valid { 67 | panic(p) 68 | } 69 | return result 70 | } 71 | } 72 | 73 | // OnceValues returns a function that invokes f only once and returns the values 74 | // returned by f. The returned function may be called concurrently. 75 | // 76 | // If f panics, the returned function will panic with the same value on every call. 77 | func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) { 78 | var once sync.Once 79 | var valid bool 80 | var p any 81 | var r1 T1 82 | var r2 T2 83 | return func() (T1, T2) { 84 | once.Do(func() { 85 | defer func() { 86 | p = recover() 87 | if !valid { 88 | panic(p) 89 | } 90 | }() 91 | r1, r2 = f() 92 | valid = true 93 | }) 94 | if !valid { 95 | panic(p) 96 | } 97 | return r1, r2 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /11.classical_problems/h2o_problem0/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "math/rand" 8 | "sort" 9 | "time" 10 | 11 | "github.com/marusama/cyclicbarrier" 12 | "golang.org/x/sync/semaphore" 13 | ) 14 | 15 | // H2O 水分子工厂. 16 | type H2O struct { 17 | // 氢原子的信号量 18 | semaH *semaphore.Weighted 19 | // 氧原子的信号量 20 | semaO *semaphore.Weighted 21 | // 等待水分子的产生 22 | b cyclicbarrier.CyclicBarrier 23 | } 24 | 25 | // 创建一个水分子工厂. 26 | func New() *H2O { 27 | return &H2O{ 28 | semaH: semaphore.NewWeighted(2), 29 | semaO: semaphore.NewWeighted(1), 30 | b: cyclicbarrier.New(3), 31 | } 32 | } 33 | 34 | // 被氢原子goroutine调用,满足条件的时候就会提供一个H原子来产生一个水分子. 35 | func (h2o *H2O) hydrogen(releaseHydrogen func()) { 36 | // 准备一个H原子填坑 37 | h2o.semaH.Acquire(context.Background(), 1) 38 | releaseHydrogen() 39 | // 等待栅栏(另一个H原子和O原子的坑填好后栅栏开启) 40 | h2o.b.Await(context.Background()) 41 | // 释放H原子的坑 42 | h2o.semaH.Release(1) 43 | } 44 | 45 | // 被氧原子goroutine调用,满足条件的时候就会一个O原子来产生一个水分子. 46 | func (h2o *H2O) oxygen(releaseOxygen func()) { 47 | // 准备一个O原子填坑 48 | h2o.semaO.Acquire(context.Background(), 1) 49 | releaseOxygen() 50 | // 等待栅栏(另两个H原子) 51 | h2o.b.Await(context.Background()) 52 | // 释放O原子的坑 53 | h2o.semaO.Release(1) 54 | } 55 | 56 | func main() { 57 | var ch chan string 58 | releaseHydrogen := func() { 59 | ch <- "H" 60 | fmt.Print("H") 61 | } 62 | releaseOxygen := func() { 63 | ch <- "O" 64 | fmt.Print("O") 65 | } 66 | 67 | // goroutine数 68 | M := 2 69 | // 每个goroutine产生的原子数 70 | N := 10 71 | ch = make(chan string, M*N*3) 72 | h2o := New() 73 | 74 | for k := 0; k < M; k++ { 75 | go func() { 76 | for i := 0; i < N; i++ { 77 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 78 | h2o.hydrogen(releaseHydrogen) 79 | } 80 | }() 81 | go func() { 82 | for i := 0; i < N; i++ { 83 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 84 | h2o.hydrogen(releaseHydrogen) 85 | } 86 | }() 87 | go func() { 88 | for i := 0; i < N; i++ { 89 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 90 | h2o.oxygen(releaseOxygen) 91 | } 92 | }() 93 | } 94 | 95 | time.Sleep(5 * time.Second) 96 | 97 | s := make([]string, 3) 98 | for i := 0; i < len(ch)/3; i++ { 99 | s[0] = <-ch 100 | s[1] = <-ch 101 | s[2] = <-ch 102 | sort.Strings(s) 103 | water := s[0] + s[1] + s[2] 104 | if water != "HHO" { 105 | log.Panicf("expect a water molecule but got %s", water) 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /4.distributed/lead_election/leader.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "math/rand" 7 | "strconv" 8 | "sync" 9 | "time" 10 | 11 | clientv3 "go.etcd.io/etcd/client/v3" 12 | "go.etcd.io/etcd/client/v3/concurrency" 13 | ) 14 | 15 | func main() { 16 | endpoints := []string{"http://127.0.0.1:2379"} 17 | 18 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | defer cli.Close() 23 | 24 | electName := "/my-election/" 25 | 26 | go watch(cli, electName) 27 | 28 | var wg sync.WaitGroup 29 | wg.Add(10) 30 | 31 | for i := 0; i < 10; i++ { 32 | go elect(i, cli, electName, &wg) 33 | } 34 | 35 | go query(cli, electName) 36 | 37 | wg.Wait() 38 | } 39 | 40 | func elect(id int, cli *clientv3.Client, electName string, wg *sync.WaitGroup) { 41 | defer wg.Done() 42 | 43 | s1, err := concurrency.NewSession(cli) 44 | if err != nil { 45 | log.Fatal(err) 46 | } 47 | defer s1.Close() 48 | e1 := concurrency.NewElection(s1, electName) 49 | 50 | time.Sleep(time.Duration(5 * time.Second)) 51 | 52 | log.Println("acampaigning for ID:", id) 53 | if err := e1.Campaign(context.Background(), strconv.Itoa(id)); err != nil { 54 | log.Fatal(err) 55 | } 56 | log.Println("campaigned for ID:", id) 57 | 58 | time.Sleep(time.Duration(rand.Intn(10)) * time.Second) 59 | 60 | if err := e1.Resign(context.TODO()); err != nil { 61 | log.Fatal(err) 62 | } 63 | log.Println("resigned for ID:", id) 64 | } 65 | 66 | func watch(cli *clientv3.Client, electName string) { 67 | s1, err := concurrency.NewSession(cli) 68 | if err != nil { 69 | log.Fatal(err) 70 | } 71 | defer s1.Close() 72 | e1 := concurrency.NewElection(s1, electName) 73 | ch := e1.Observe(context.TODO()) 74 | 75 | for i := 0; i < 10; i++ { 76 | resp := <-ch 77 | log.Println("leader changed: to", string(resp.Kvs[0].Key), string(resp.Kvs[0].Value)) 78 | } 79 | } 80 | 81 | func query(cli *clientv3.Client, electName string) { 82 | s1, err := concurrency.NewSession(cli) 83 | if err != nil { 84 | log.Fatal(err) 85 | } 86 | defer s1.Close() 87 | e1 := concurrency.NewElection(s1, electName) 88 | 89 | for i := 0; i < 10; i++ { 90 | resp, err := e1.Leader(context.Background()) 91 | if err != nil { 92 | log.Printf("failed to get the current leader: %v", err) 93 | time.Sleep(9 * time.Second) 94 | continue 95 | } 96 | log.Println("current leader:", string(resp.Kvs[0].Key), string(resp.Kvs[0].Value)) 97 | time.Sleep(9 * time.Second) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /11.classical_problems/dining_philosophers_problem1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "math/rand" 7 | "net/http" 8 | _ "net/http/pprof" 9 | "os" 10 | "os/signal" 11 | "sync" 12 | "syscall" 13 | "time" 14 | 15 | "github.com/fatih/color" 16 | "golang.org/x/sync/semaphore" 17 | ) 18 | 19 | func init() { 20 | rand.Seed(time.Now().UTC().UnixNano()) 21 | } 22 | 23 | // Chopstick 代表筷子. 24 | type Chopstick struct{ sync.Mutex } 25 | 26 | // Philosopher 代表哲学家. 27 | type Philosopher struct { 28 | // 哲学家的名字 29 | name string 30 | // 左手一只和右手一只筷子 31 | leftChopstick, rightChopstick *Chopstick 32 | status string 33 | 34 | sema *semaphore.Weighted 35 | } 36 | 37 | // 无休止的进餐和冥想. 38 | // 吃完睡(冥想、打坐), 睡完吃. 39 | // 可以调整吃睡的时间来增加或者减少抢夺叉子的机会. 40 | func (p *Philosopher) dine() { 41 | for { 42 | mark(p, "冥想") 43 | randomPause(10) 44 | 45 | mark(p, "饿了") 46 | p.sema.Acquire(context.Background(), 1) 47 | p.leftChopstick.Lock() // 先尝试拿起左手边的筷子 48 | mark(p, "拿起左手筷子") 49 | p.rightChopstick.Lock() // 再尝试拿起右手边的筷子 50 | mark(p, "拿起右手筷子") 51 | 52 | mark(p, "用膳") 53 | randomPause(10) 54 | 55 | p.rightChopstick.Unlock() // 先尝试放下右手边的筷子 56 | p.leftChopstick.Unlock() // 再尝试拿起左手边的筷子 57 | p.sema.Release(1) 58 | } 59 | } 60 | 61 | // 随机暂停一段时 62 | func randomPause(max int) { 63 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(max))) 64 | } 65 | 66 | // 显示此哲学家的状态 67 | func mark(p *Philosopher, action string) { 68 | fmt.Printf("%s开始%s\n", p.name, action) 69 | p.status = fmt.Sprintf("%s开始%s\n", p.name, action) 70 | } 71 | 72 | func main() { 73 | go http.ListenAndServe("localhost:8972", nil) 74 | 75 | // 哲学家数量 76 | count := 5 77 | 78 | // 创建5根筷子 79 | chopsticks := make([]*Chopstick, count) 80 | for i := 0; i < count; i++ { 81 | chopsticks[i] = new(Chopstick) 82 | } 83 | 84 | sema := semaphore.NewWeighted(4) 85 | // 86 | names := []string{color.RedString("孔子"), color.MagentaString("庄子"), color.CyanString("墨子"), color.GreenString("孙子"), color.WhiteString("老子")} 87 | 88 | // 创建哲学家, 分配给他们左右手边的叉子,领他们做到圆餐桌上. 89 | philosophers := make([]*Philosopher, count) 90 | for i := 0; i < count; i++ { 91 | philosophers[i] = &Philosopher{ 92 | name: names[i], leftChopstick: chopsticks[i], rightChopstick: chopsticks[(i+1)%count], 93 | sema: sema, 94 | } 95 | go philosophers[i].dine() 96 | } 97 | 98 | sigs := make(chan os.Signal, 1) 99 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 100 | <-sigs 101 | 102 | fmt.Println("退出中... 每个哲学家的状态:") 103 | for _, p := range philosophers { 104 | fmt.Print(p.status) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /11.classical_problems/h2o2_problem3/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "math/rand" 7 | "sort" 8 | "time" 9 | 10 | "github.com/marusama/cyclicbarrier" 11 | "golang.org/x/sync/semaphore" 12 | ) 13 | 14 | // H2O 双氧水. 15 | type H2O2 struct { 16 | // 氢原子的信号量 17 | semaH *semaphore.Weighted 18 | // 氧原子的信号量 19 | semaO *semaphore.Weighted 20 | // 等待水分子的产生 21 | b cyclicbarrier.CyclicBarrier 22 | } 23 | 24 | // 创建一个水分子工厂. 25 | func New() *H2O2 { 26 | return &H2O2{ 27 | semaH: semaphore.NewWeighted(2), 28 | semaO: semaphore.NewWeighted(2), 29 | b: cyclicbarrier.New(4), 30 | } 31 | } 32 | 33 | // 被氢原子goroutine调用,满足条件的时候就会提供一个H原子来产生一个双氧水分子. 34 | func (h2o *H2O2) hydrogen(releaseHydrogen func()) { 35 | // 准备一个H原子填坑 36 | h2o.semaH.Acquire(context.Background(), 1) 37 | releaseHydrogen() 38 | // 等待栅栏(另一个H原子和O原子的坑填好后栅栏开启) 39 | h2o.b.Await(context.Background()) 40 | // 释放H原子的坑 41 | h2o.semaH.Release(1) 42 | } 43 | 44 | // 被氧原子goroutine调用,满足条件的时候就会一个O原子来产生一个双氧水分子. 45 | func (h2o *H2O2) oxygen(releaseOxygen func()) { 46 | // 准备一个O原子填坑 47 | h2o.semaO.Acquire(context.Background(), 1) 48 | releaseOxygen() 49 | // 等待栅栏(另两个H原子) 50 | h2o.b.Await(context.Background()) 51 | // 释放O原子的坑 52 | h2o.semaO.Release(1) 53 | } 54 | 55 | func main() { 56 | var ch chan string 57 | releaseHydrogen := func() { 58 | ch <- "H" 59 | } 60 | releaseOxygen := func() { 61 | ch <- "O" 62 | } 63 | 64 | // goroutine数 65 | M := 10 66 | // 每个goroutine产生的原子数 67 | N := 100 68 | ch = make(chan string, M*N*3) 69 | h2o2 := New() 70 | for k := 0; k < M; k++ { 71 | go func() { 72 | for i := 0; i < N; i++ { 73 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 74 | h2o2.hydrogen(releaseHydrogen) 75 | } 76 | }() 77 | go func() { 78 | for i := 0; i < N; i++ { 79 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 80 | h2o2.hydrogen(releaseHydrogen) 81 | } 82 | }() 83 | go func() { 84 | for i := 0; i < N; i++ { 85 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 86 | h2o2.oxygen(releaseOxygen) 87 | } 88 | }() 89 | go func() { 90 | for i := 0; i < N; i++ { 91 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 92 | h2o2.oxygen(releaseOxygen) 93 | } 94 | }() 95 | } 96 | 97 | time.Sleep(10 * time.Second) 98 | 99 | s := make([]string, 4) 100 | for i := 0; i < len(ch)/4; i++ { 101 | s[0] = <-ch 102 | s[1] = <-ch 103 | s[2] = <-ch 104 | s[3] = <-ch 105 | sort.Strings(s) 106 | water := s[0] + s[1] + s[2] + s[3] 107 | if water != "HHOO" { 108 | log.Panicf("expect a water molecule but got %s", water) 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /11.classical_problems/dining_philosophers_problem2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "net/http" 7 | _ "net/http/pprof" 8 | "os" 9 | "os/signal" 10 | "sync" 11 | "syscall" 12 | "time" 13 | 14 | "github.com/fatih/color" 15 | ) 16 | 17 | func init() { 18 | rand.Seed(time.Now().UTC().UnixNano()) 19 | } 20 | 21 | // Chopstick 代表筷子. 22 | type Chopstick struct{ sync.Mutex } 23 | 24 | // Philosopher 代表哲学家. 25 | type Philosopher struct { 26 | ID int 27 | // 哲学家的名字 28 | name string 29 | // 左手一只和右手一只筷子 30 | leftChopstick, rightChopstick *Chopstick 31 | status string 32 | } 33 | 34 | // 无休止的进餐和冥想. 35 | // 吃完睡(冥想、打坐), 睡完吃. 36 | // 可以调整吃睡的时间来增加或者减少抢夺叉子的机会. 37 | func (p *Philosopher) dine() { 38 | for { 39 | mark(p, "冥想") 40 | randomPause(10) 41 | 42 | mark(p, "饿了") 43 | if p.ID%2 == 1 { // 奇数 44 | p.leftChopstick.Lock() // 先尝试拿起左手边的筷子 45 | mark(p, "拿起左手筷子") 46 | p.rightChopstick.Lock() // 再尝试拿起右手边的筷子 47 | 48 | mark(p, "用膳") 49 | randomPause(10) 50 | 51 | p.rightChopstick.Unlock() // 先尝试放下右手边的筷子 52 | p.leftChopstick.Unlock() // 再尝试拿起左手边的筷子 53 | } else { 54 | p.rightChopstick.Lock() // 先尝试拿起左手边的筷子 55 | mark(p, "拿起右手筷子") 56 | p.leftChopstick.Lock() // 再尝试拿起右手边的筷子 57 | 58 | mark(p, "用膳") 59 | randomPause(10) 60 | 61 | p.leftChopstick.Unlock() // 先尝试放下右手边的筷子 62 | p.rightChopstick.Unlock() // 再尝试拿起左手边的筷子 63 | } 64 | 65 | } 66 | } 67 | 68 | // 随机暂停一段时 69 | func randomPause(max int) { 70 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(max))) 71 | } 72 | 73 | // 显示此哲学家的状态 74 | func mark(p *Philosopher, action string) { 75 | fmt.Printf("%s开始%s\n", p.name, action) 76 | p.status = fmt.Sprintf("%s开始%s\n", p.name, action) 77 | } 78 | 79 | func main() { 80 | go http.ListenAndServe("localhost:8972", nil) 81 | 82 | // 哲学家数量 83 | count := 5 84 | 85 | // 创建5根筷子 86 | chopsticks := make([]*Chopstick, count) 87 | for i := 0; i < count; i++ { 88 | chopsticks[i] = new(Chopstick) 89 | } 90 | 91 | // 92 | names := []string{color.RedString("孔子"), color.MagentaString("庄子"), color.CyanString("墨子"), color.GreenString("孙子"), color.WhiteString("老子")} 93 | 94 | // 创建哲学家, 分配给他们左右手边的叉子,领他们做到圆餐桌上. 95 | philosophers := make([]*Philosopher, count) 96 | for i := 0; i < count; i++ { 97 | philosophers[i] = &Philosopher{ 98 | ID: i + 1, 99 | name: names[i], leftChopstick: chopsticks[i], rightChopstick: chopsticks[(i+1)%count], 100 | } 101 | go philosophers[i].dine() 102 | } 103 | 104 | sigs := make(chan os.Signal, 1) 105 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 106 | <-sigs 107 | 108 | fmt.Println("退出中... 每个哲学家的状态:") 109 | for _, p := range philosophers { 110 | fmt.Print(p.status) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /11.classical_problems/dining_philosophers_problem3/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "net/http" 7 | _ "net/http/pprof" 8 | "os" 9 | "os/signal" 10 | "sync" 11 | "syscall" 12 | "time" 13 | 14 | "github.com/fatih/color" 15 | ) 16 | 17 | func init() { 18 | rand.Seed(time.Now().UTC().UnixNano()) 19 | } 20 | 21 | // Chopstick 代表筷子. 22 | type Chopstick struct{ sync.Mutex } 23 | 24 | // Philosopher 代表哲学家. 25 | type Philosopher struct { 26 | ID int 27 | // 哲学家的名字 28 | name string 29 | // 左手一只和右手一只筷子 30 | leftChopstick, rightChopstick *Chopstick 31 | status string 32 | } 33 | 34 | // 无休止的进餐和冥想. 35 | // 吃完睡(冥想、打坐), 睡完吃. 36 | // 可以调整吃睡的时间来增加或者减少抢夺叉子的机会. 37 | func (p *Philosopher) dine() { 38 | for { 39 | mark(p, "冥想") 40 | randomPause(10) 41 | 42 | mark(p, "饿了") 43 | if p.ID == 5 { // 44 | p.rightChopstick.Lock() // 先尝试拿起第1只筷子 45 | mark(p, "拿起左手筷子") 46 | p.leftChopstick.Lock() // 再尝试拿起第5只筷子 47 | 48 | mark(p, "用膳") 49 | randomPause(10) 50 | 51 | p.leftChopstick.Unlock() // 先尝试放下第5只的筷子 52 | p.rightChopstick.Unlock() // 再尝试放下第1只的筷子 53 | } else { 54 | p.leftChopstick.Lock() // 先尝试拿起左手边的筷子(第n只) 55 | mark(p, "拿起右手筷子") 56 | p.rightChopstick.Lock() // 再尝试拿起右手边的筷子(第n+1只) 57 | 58 | mark(p, "用膳") 59 | randomPause(10) 60 | 61 | p.rightChopstick.Unlock() // 先尝试放下右手边的筷子 62 | p.leftChopstick.Unlock() // 再尝试拿起左手边的筷子 63 | } 64 | 65 | } 66 | } 67 | 68 | // 随机暂停一段时 69 | func randomPause(max int) { 70 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(max))) 71 | } 72 | 73 | // 显示此哲学家的状态 74 | func mark(p *Philosopher, action string) { 75 | fmt.Printf("%s开始%s\n", p.name, action) 76 | p.status = fmt.Sprintf("%s开始%s\n", p.name, action) 77 | } 78 | 79 | func main() { 80 | go http.ListenAndServe("localhost:8972", nil) 81 | 82 | // 哲学家数量 83 | count := 5 84 | 85 | // 创建5根筷子 86 | chopsticks := make([]*Chopstick, count) 87 | for i := 0; i < count; i++ { 88 | chopsticks[i] = new(Chopstick) 89 | } 90 | 91 | // 92 | names := []string{color.RedString("孔子"), color.MagentaString("庄子"), color.CyanString("墨子"), color.GreenString("孙子"), color.WhiteString("老子")} 93 | 94 | // 创建哲学家, 分配给他们左右手边的叉子,领他们做到圆餐桌上. 95 | philosophers := make([]*Philosopher, count) 96 | for i := 0; i < count; i++ { 97 | philosophers[i] = &Philosopher{ 98 | ID: i + 1, 99 | name: names[i], leftChopstick: chopsticks[i], rightChopstick: chopsticks[(i+1)%count], 100 | } 101 | go philosophers[i].dine() 102 | } 103 | 104 | sigs := make(chan os.Signal, 1) 105 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 106 | <-sigs 107 | 108 | fmt.Println("退出中... 每个哲学家的状态:") 109 | for _, p := range philosophers { 110 | fmt.Print(p.status) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /5.channel/fanin/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "sync" 7 | ) 8 | 9 | // https://github.com/campoy/justforfunc/blob/master/27-merging-chans/main.go 10 | 11 | func fanIn(chans ...<-chan any) <-chan any { 12 | out := make(chan any) 13 | go func() { 14 | var wg sync.WaitGroup 15 | wg.Add(len(chans)) 16 | 17 | for _, c := range chans { 18 | go func(c <-chan any) { 19 | for v := range c { 20 | out <- v 21 | } 22 | wg.Done() 23 | }(c) 24 | } 25 | 26 | wg.Wait() 27 | close(out) 28 | }() 29 | return out 30 | } 31 | 32 | func fanInReflect(chans ...<-chan any) <-chan any { 33 | out := make(chan any) 34 | go func() { 35 | defer close(out) 36 | var cases []reflect.SelectCase 37 | for _, c := range chans { 38 | cases = append(cases, reflect.SelectCase{ 39 | Dir: reflect.SelectRecv, 40 | Chan: reflect.ValueOf(c), 41 | }) 42 | } 43 | 44 | for len(cases) > 0 { 45 | i, v, ok := reflect.Select(cases) 46 | if !ok { //remove this case 47 | cases = append(cases[:i], cases[i+1:]...) 48 | continue 49 | } 50 | out <- v.Interface() 51 | } 52 | }() 53 | return out 54 | 55 | } 56 | 57 | func fanInRec(chans ...<-chan any) <-chan any { 58 | switch len(chans) { 59 | case 0: 60 | c := make(chan any) 61 | close(c) 62 | return c 63 | case 1: 64 | return chans[0] 65 | case 2: 66 | return mergeTwo(chans[0], chans[1]) 67 | default: 68 | m := len(chans) / 2 69 | return mergeTwo( 70 | fanInRec(chans[:m]...), 71 | fanInRec(chans[m:]...)) 72 | } 73 | } 74 | 75 | func mergeTwo(a, b <-chan any) <-chan any { 76 | c := make(chan any) 77 | 78 | go func() { 79 | defer close(c) 80 | for a != nil || b != nil { 81 | select { 82 | case v, ok := <-a: 83 | if !ok { 84 | a = nil 85 | continue 86 | } 87 | c <- v 88 | case v, ok := <-b: 89 | if !ok { 90 | b = nil 91 | continue 92 | } 93 | c <- v 94 | } 95 | } 96 | }() 97 | return c 98 | } 99 | 100 | func asStream(done <-chan struct{}) <-chan any { 101 | s := make(chan any) 102 | values := []int{1, 2, 3, 4, 5} 103 | go func() { 104 | defer close(s) 105 | 106 | for _, v := range values { 107 | select { 108 | case <-done: 109 | return 110 | case s <- v: 111 | } 112 | } 113 | 114 | }() 115 | return s 116 | } 117 | 118 | func main() { 119 | fmt.Println("fanIn by goroutine:") 120 | done := make(chan struct{}) 121 | ch := fanIn(asStream(done), asStream(done), asStream(done)) 122 | for v := range ch { 123 | fmt.Println(v) 124 | } 125 | 126 | fmt.Println("fanIn by reflect:") 127 | ch = fanInReflect(asStream(done), asStream(done), asStream(done)) 128 | for v := range ch { 129 | fmt.Println(v) 130 | } 131 | 132 | fmt.Println("fanIn by recursion:") 133 | ch = fanInRec(asStream(done), asStream(done), asStream(done)) 134 | for v := range ch { 135 | fmt.Println(v) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /7.orchestration/fizzbuzz3/fizzbuzz.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sync" 7 | 8 | "github.com/marusama/cyclicbarrier" 9 | ) 10 | 11 | // 编写一个可以从 1 到 n 输出代表这个数字的字符串的程序,要求: 12 | 13 | // 如果这个数字可以被 3 整除,输出 "fizz"。 14 | // 如果这个数字可以被 5 整除,输出 "buzz"。 15 | // 如果这个数字可以同时被 3 和 5 整除,输出 "fizzbuzz"。 16 | // 例如,当 n = 15,输出: 1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11, fizz, 13, 14, fizzbuzz。 17 | 18 | // 假设有这么一个结构体: 19 | // type FizzBuzz struct {} 20 | // func (fb *FizzBuzz) fizz() {} 21 | // func (fb *FizzBuzz) buzz() {} 22 | // func (fb *FizzBuzz) fizzbuzz() {} 23 | // func (fb *FizzBuzz) number() {} 24 | 25 | // 请你实现一个有四个线程的多协程版 FizzBuzz,同一个 FizzBuzz 对象会被如下四个协程使用: 26 | 27 | // 协程A将调用 fizz() 来判断是否能被 3 整除,如果可以,则输出 fizz。 28 | // 协程B将调用 buzz() 来判断是否能被 5 整除,如果可以,则输出 buzz。 29 | // 协程C将调用 fizzbuzz() 来判断是否同时能被 3 和 5 整除,如果可以,则输出 fizzbuzz。 30 | // 协程D将调用 number() 来实现输出既不能被 3 整除也不能被 5 整除的数字。 31 | 32 | type FizzBuzz struct { 33 | n int 34 | 35 | barrier cyclicbarrier.CyclicBarrier 36 | wg sync.WaitGroup 37 | } 38 | 39 | func New(n int) *FizzBuzz { 40 | return &FizzBuzz{ 41 | n: n, 42 | barrier: cyclicbarrier.New(4), 43 | } 44 | } 45 | 46 | func (fb *FizzBuzz) start() { 47 | fb.wg.Add(4) 48 | 49 | go fb.fizz() 50 | go fb.buzz() 51 | go fb.fizzbuzz() 52 | go fb.number() 53 | 54 | fb.wg.Wait() 55 | } 56 | 57 | func (fb *FizzBuzz) fizz() { 58 | defer fb.wg.Done() 59 | 60 | ctx := context.Background() 61 | v := 0 62 | for { 63 | fb.barrier.Await(ctx) 64 | v++ 65 | 66 | if v > fb.n { 67 | return 68 | } 69 | 70 | if v%3 == 0 { 71 | if v%5 == 0 { 72 | continue 73 | } 74 | 75 | if v == fb.n { 76 | fmt.Print(" fizz。") 77 | } else { 78 | fmt.Print(" fizz,") 79 | } 80 | } 81 | 82 | } 83 | 84 | } 85 | func (fb *FizzBuzz) buzz() { 86 | defer fb.wg.Done() 87 | 88 | ctx := context.Background() 89 | 90 | v := 0 91 | for { 92 | fb.barrier.Await(ctx) 93 | v++ 94 | 95 | if v > fb.n { 96 | return 97 | } 98 | 99 | if v%5 == 0 { 100 | if v%3 == 0 { 101 | continue 102 | } 103 | 104 | if v == fb.n { 105 | fmt.Print(" buzz。") 106 | } else { 107 | fmt.Print(" buzz,") 108 | } 109 | } 110 | } 111 | } 112 | func (fb *FizzBuzz) fizzbuzz() { 113 | defer fb.wg.Done() 114 | 115 | ctx := context.Background() 116 | v := 0 117 | for { 118 | fb.barrier.Await(ctx) 119 | v++ 120 | 121 | if v > fb.n { 122 | return 123 | } 124 | 125 | if v%5 == 0 && v%3 == 0 { 126 | if v == fb.n { 127 | fmt.Print(" fizzbuzz。") 128 | } else { 129 | fmt.Print(" fizzbuzz,") 130 | } 131 | } 132 | } 133 | } 134 | func (fb *FizzBuzz) number() { 135 | defer fb.wg.Done() 136 | 137 | ctx := context.Background() 138 | v := 0 139 | for { 140 | fb.barrier.Await(ctx) 141 | v++ 142 | 143 | if v > fb.n { 144 | return 145 | } 146 | 147 | if v%5 != 0 && v%3 != 0 { 148 | if v == fb.n { 149 | fmt.Printf(" %d。", v) 150 | } else { 151 | fmt.Printf(" %d,", v) 152 | } 153 | } 154 | 155 | } 156 | } 157 | 158 | func main() { 159 | fb := New(15) 160 | fb.start() 161 | } 162 | -------------------------------------------------------------------------------- /7.orchestration/fizzbuzz2/fizzbuzz.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | // 编写一个可以从 1 到 n 输出代表这个数字的字符串的程序,要求: 9 | 10 | // 如果这个数字可以被 3 整除,输出 "fizz"。 11 | // 如果这个数字可以被 5 整除,输出 "buzz"。 12 | // 如果这个数字可以同时被 3 和 5 整除,输出 "fizzbuzz"。 13 | // 例如,当 n = 15,输出: 1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11, fizz, 13, 14, fizzbuzz。 14 | 15 | // 假设有这么一个结构体: 16 | // type FizzBuzz struct {} 17 | // func (fb *FizzBuzz) fizz() {} 18 | // func (fb *FizzBuzz) buzz() {} 19 | // func (fb *FizzBuzz) fizzbuzz() {} 20 | // func (fb *FizzBuzz) number() {} 21 | 22 | // 请你实现一个有四个线程的多协程版 FizzBuzz,同一个 FizzBuzz 对象会被如下四个协程使用: 23 | 24 | // 协程A将调用 fizz() 来判断是否能被 3 整除,如果可以,则输出 fizz。 25 | // 协程B将调用 buzz() 来判断是否能被 5 整除,如果可以,则输出 buzz。 26 | // 协程C将调用 fizzbuzz() 来判断是否同时能被 3 和 5 整除,如果可以,则输出 fizzbuzz。 27 | // 协程D将调用 number() 来实现输出既不能被 3 整除也不能被 5 整除的数字。 28 | 29 | type FizzBuzz struct { 30 | n int 31 | 32 | ch chan int 33 | wg sync.WaitGroup 34 | } 35 | 36 | func New(n int) *FizzBuzz { 37 | return &FizzBuzz{ 38 | n: n, 39 | ch: make(chan int, 1), 40 | } 41 | } 42 | 43 | func (fb *FizzBuzz) start() { 44 | fb.wg.Add(4) 45 | 46 | go fb.fizz() 47 | go fb.buzz() 48 | go fb.fizzbuzz() 49 | go fb.number() 50 | 51 | fb.ch <- 1 52 | 53 | fb.wg.Wait() 54 | } 55 | 56 | func (fb *FizzBuzz) fizz() { 57 | defer fb.wg.Done() 58 | 59 | for v := range fb.ch { 60 | if v > fb.n { 61 | fb.ch <- v 62 | return 63 | } 64 | 65 | if v%3 == 0 { 66 | if v%5 == 0 { 67 | fb.ch <- v 68 | continue 69 | } 70 | 71 | if v == fb.n { 72 | fmt.Print(" fizz。") 73 | } else { 74 | fmt.Print(" fizz,") 75 | } 76 | 77 | fb.ch <- v + 1 78 | continue 79 | } 80 | 81 | fb.ch <- v 82 | } 83 | 84 | } 85 | func (fb *FizzBuzz) buzz() { 86 | defer fb.wg.Done() 87 | 88 | for v := range fb.ch { 89 | 90 | if v > fb.n { 91 | fb.ch <- v 92 | return 93 | } 94 | 95 | if v%5 == 0 { 96 | if v%3 == 0 { 97 | fb.ch <- v 98 | continue 99 | } 100 | 101 | if v == fb.n { 102 | fmt.Print(" buzz。") 103 | } else { 104 | fmt.Print(" buzz,") 105 | } 106 | 107 | fb.ch <- v + 1 108 | continue 109 | } 110 | 111 | fb.ch <- v 112 | } 113 | } 114 | func (fb *FizzBuzz) fizzbuzz() { 115 | defer fb.wg.Done() 116 | 117 | for v := range fb.ch { 118 | 119 | if v > fb.n { 120 | fb.ch <- v 121 | return 122 | } 123 | 124 | if v%5 == 0 && v%3 == 0 { 125 | if v == fb.n { 126 | fmt.Print(" fizzbuzz。") 127 | } else { 128 | fmt.Print(" fizzbuzz,") 129 | } 130 | 131 | fb.ch <- v + 1 132 | continue 133 | } 134 | 135 | fb.ch <- v 136 | } 137 | } 138 | func (fb *FizzBuzz) number() { 139 | defer fb.wg.Done() 140 | 141 | for v := range fb.ch { 142 | 143 | if v > fb.n { 144 | fb.ch <- v 145 | return 146 | } 147 | 148 | if v%5 != 0 && v%3 != 0 { 149 | if v == fb.n { 150 | fmt.Printf(" %d。", v) 151 | } else { 152 | fmt.Printf(" %d,", v) 153 | } 154 | 155 | fb.ch <- v + 1 156 | continue 157 | } 158 | 159 | fb.ch <- v 160 | 161 | } 162 | } 163 | 164 | func main() { 165 | fb := New(15) 166 | fb.start() 167 | } 168 | -------------------------------------------------------------------------------- /5.channel/eapache/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/eapache/channels" 7 | ) 8 | 9 | func testPipe() { 10 | fmt.Println("pipe:") 11 | a := channels.NewNativeChannel(channels.None) 12 | b := channels.NewNativeChannel(channels.None) 13 | 14 | channels.Pipe(a, b) 15 | // channels.WeakPipe(a, b) 16 | 17 | go func() { 18 | for i := 0; i < 5; i++ { 19 | a.In() <- i 20 | } 21 | a.Close() 22 | }() 23 | 24 | for v := range b.Out() { 25 | fmt.Printf("%d ", v) 26 | } 27 | } 28 | 29 | func testDist() { 30 | fmt.Println("dist:") 31 | a := channels.NewNativeChannel(channels.None) 32 | outputs := []channels.Channel{ 33 | channels.NewNativeChannel(channels.None), 34 | channels.NewNativeChannel(channels.None), 35 | channels.NewNativeChannel(channels.None), 36 | channels.NewNativeChannel(channels.None), 37 | } 38 | 39 | channels.Distribute(a, outputs[0], outputs[1], outputs[2], outputs[3]) 40 | //channels.WeakDistribute(a, outputs[0], outputs[1], outputs[2], outputs[3]) 41 | 42 | go func() { 43 | for i := 0; i < 5; i++ { 44 | a.In() <- i 45 | } 46 | a.Close() 47 | }() 48 | 49 | for i := 0; i < 6; i++ { 50 | var v any 51 | var j int 52 | select { 53 | case v = <-outputs[0].Out(): 54 | j = 0 55 | case v = <-outputs[1].Out(): 56 | j = 1 57 | case v = <-outputs[2].Out(): 58 | j = 2 59 | case v = <-outputs[3].Out(): 60 | j = 3 61 | } 62 | fmt.Printf("channel#%d: %d\n", j, v) 63 | } 64 | 65 | } 66 | 67 | func testTee() { 68 | fmt.Println("tee:") 69 | a := channels.NewNativeChannel(channels.None) 70 | outputs := []channels.Channel{ 71 | channels.NewNativeChannel(channels.None), 72 | channels.NewNativeChannel(channels.None), 73 | channels.NewNativeChannel(channels.None), 74 | channels.NewNativeChannel(channels.None), 75 | } 76 | 77 | channels.Tee(a, outputs[0], outputs[1], outputs[2], outputs[3]) 78 | //channels.WeakTee(a, outputs[0], outputs[1], outputs[2], outputs[3]) 79 | 80 | go func() { 81 | for i := 0; i < 5; i++ { 82 | a.In() <- i 83 | } 84 | a.Close() 85 | }() 86 | 87 | for i := 0; i < 20; i++ { 88 | var v any 89 | var j int 90 | select { 91 | case v = <-outputs[0].Out(): 92 | j = 0 93 | case v = <-outputs[1].Out(): 94 | j = 1 95 | case v = <-outputs[2].Out(): 96 | j = 2 97 | case v = <-outputs[3].Out(): 98 | j = 3 99 | } 100 | fmt.Printf("channel#%d: %d\n", j, v) 101 | } 102 | } 103 | 104 | func testMulti() { 105 | fmt.Println("multi:") 106 | a := channels.NewNativeChannel(channels.None) 107 | inputs := []channels.Channel{ 108 | channels.NewNativeChannel(channels.None), 109 | channels.NewNativeChannel(channels.None), 110 | channels.NewNativeChannel(channels.None), 111 | channels.NewNativeChannel(channels.None), 112 | } 113 | 114 | channels.Multiplex(a, inputs[0], inputs[1], inputs[2], inputs[3]) 115 | //channels.WeakMultiplex(a, inputs[0], inputs[1], inputs[2], inputs[3]) 116 | 117 | go func() { 118 | for i := 0; i < 5; i++ { 119 | for j := range inputs { 120 | inputs[j].In() <- i 121 | } 122 | } 123 | for i := range inputs { 124 | inputs[i].Close() 125 | } 126 | }() 127 | 128 | for v := range a.Out() { 129 | fmt.Printf("%d ", v) 130 | } 131 | } 132 | 133 | func main() { 134 | testPipe() 135 | testDist() 136 | testTee() 137 | testMulti() 138 | } 139 | -------------------------------------------------------------------------------- /7.orchestration/fizzbuzz1/fizzbuzz.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | // 编写一个可以从 1 到 n 输出代表这个数字的字符串的程序,要求: 9 | 10 | // 如果这个数字可以被 3 整除,输出 "fizz"。 11 | // 如果这个数字可以被 5 整除,输出 "buzz"。 12 | // 如果这个数字可以同时被 3 和 5 整除,输出 "fizzbuzz"。 13 | // 例如,当 n = 15,输出: 1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11, fizz, 13, 14, fizzbuzz。 14 | 15 | // 假设有这么一个结构体: 16 | // type FizzBuzz struct {} 17 | // func (fb *FizzBuzz) fizz() {} 18 | // func (fb *FizzBuzz) buzz() {} 19 | // func (fb *FizzBuzz) fizzbuzz() {} 20 | // func (fb *FizzBuzz) number() {} 21 | 22 | // 请你实现一个有四个线程的多协程版 FizzBuzz,同一个 FizzBuzz 对象会被如下四个协程使用: 23 | 24 | // 协程A将调用 fizz() 来判断是否能被 3 整除,如果可以,则输出 fizz。 25 | // 协程B将调用 buzz() 来判断是否能被 5 整除,如果可以,则输出 buzz。 26 | // 协程C将调用 fizzbuzz() 来判断是否同时能被 3 和 5 整除,如果可以,则输出 fizzbuzz。 27 | // 协程D将调用 number() 来实现输出既不能被 3 整除也不能被 5 整除的数字。 28 | 29 | // 击鼓传话式 30 | 31 | type FizzBuzz struct { 32 | n int 33 | 34 | chs []chan int 35 | wg sync.WaitGroup 36 | } 37 | 38 | func New(n int) *FizzBuzz { 39 | chs := make([]chan int, 4) 40 | for i := 0; i < 4; i++ { 41 | chs[i] = make(chan int, 1) 42 | } 43 | 44 | return &FizzBuzz{ 45 | n: n, 46 | chs: chs, 47 | } 48 | } 49 | 50 | func (fb *FizzBuzz) start() { 51 | fb.wg.Add(4) 52 | 53 | go fb.fizz() 54 | go fb.buzz() 55 | go fb.fizzbuzz() 56 | go fb.number() 57 | 58 | fb.chs[0] <- 1 59 | 60 | fb.wg.Wait() 61 | } 62 | 63 | func (fb *FizzBuzz) fizz() { 64 | defer fb.wg.Done() 65 | 66 | next := fb.chs[1] 67 | for v := range fb.chs[0] { 68 | if v > fb.n { 69 | next <- v 70 | return 71 | } 72 | 73 | if v%3 == 0 { 74 | if v%5 == 0 { 75 | next <- v 76 | continue 77 | } 78 | 79 | if v == fb.n { 80 | fmt.Print(" fizz。") 81 | } else { 82 | fmt.Print(" fizz,") 83 | } 84 | 85 | next <- v + 1 86 | continue 87 | } 88 | 89 | next <- v 90 | } 91 | 92 | } 93 | func (fb *FizzBuzz) buzz() { 94 | defer fb.wg.Done() 95 | 96 | next := fb.chs[2] 97 | for v := range fb.chs[1] { 98 | 99 | if v > fb.n { 100 | next <- v 101 | return 102 | } 103 | 104 | if v%5 == 0 { 105 | if v%3 == 0 { 106 | next <- v 107 | continue 108 | } 109 | 110 | if v == fb.n { 111 | fmt.Print(" buzz。") 112 | } else { 113 | fmt.Print(" buzz,") 114 | } 115 | 116 | next <- v + 1 117 | continue 118 | } 119 | 120 | next <- v 121 | } 122 | } 123 | func (fb *FizzBuzz) fizzbuzz() { 124 | defer fb.wg.Done() 125 | 126 | next := fb.chs[3] 127 | for v := range fb.chs[2] { 128 | 129 | if v > fb.n { 130 | next <- v 131 | return 132 | } 133 | 134 | if v%5 == 0 && v%3 == 0 { 135 | if v == fb.n { 136 | fmt.Print(" fizzbuzz。") 137 | } else { 138 | fmt.Print(" fizzbuzz,") 139 | } 140 | 141 | next <- v + 1 142 | continue 143 | } 144 | 145 | next <- v 146 | } 147 | } 148 | func (fb *FizzBuzz) number() { 149 | defer fb.wg.Done() 150 | 151 | next := fb.chs[0] 152 | for v := range fb.chs[3] { 153 | 154 | if v > fb.n { 155 | next <- v 156 | return 157 | } 158 | 159 | if v%5 != 0 && v%3 != 0 { 160 | if v == fb.n { 161 | fmt.Printf(" %d。", v) 162 | } else { 163 | fmt.Printf(" %d,", v) 164 | } 165 | 166 | next <- v + 1 167 | continue 168 | } 169 | 170 | next <- v 171 | 172 | } 173 | } 174 | 175 | func main() { 176 | fb := New(15) 177 | fb.start() 178 | } 179 | -------------------------------------------------------------------------------- /5.channel/stream/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func asStream[T any](done <-chan struct{}, values ...T) <-chan T { 8 | s := make(chan T) 9 | go func() { 10 | defer close(s) 11 | 12 | for _, v := range values { 13 | select { 14 | case <-done: 15 | return 16 | case s <- v: 17 | } 18 | } 19 | 20 | }() 21 | return s 22 | } 23 | func asRepeatStream[T any](done <-chan struct{}, values ...T) <-chan T { 24 | s := make(chan T) 25 | go func() { 26 | defer close(s) 27 | for { 28 | for _, v := range values { 29 | select { 30 | case <-done: 31 | return 32 | case s <- v: 33 | } 34 | } 35 | } 36 | }() 37 | return s 38 | } 39 | 40 | func takeN[T any](done <-chan struct{}, valueStream <-chan T, num int) <-chan T { 41 | takeStream := make(chan T) 42 | go func() { 43 | defer close(takeStream) 44 | for i := 0; i < num; i++ { 45 | select { 46 | case <-done: 47 | return 48 | case takeStream <- <-valueStream: 49 | } 50 | } 51 | }() 52 | return takeStream 53 | } 54 | 55 | func takeFn[T any](done <-chan struct{}, valueStream <-chan T, fn func(any) bool) <-chan T { 56 | takeStream := make(chan T) 57 | go func() { 58 | defer close(takeStream) 59 | for { 60 | select { 61 | case <-done: 62 | return 63 | case v := <-valueStream: 64 | if fn(v) { 65 | takeStream <- v 66 | } 67 | } 68 | } 69 | }() 70 | return takeStream 71 | } 72 | 73 | func takeWhile[T any](done <-chan struct{}, valueStream <-chan T, fn func(any) bool) <-chan T { 74 | takeStream := make(chan T) 75 | go func() { 76 | defer close(takeStream) 77 | for { 78 | select { 79 | case <-done: 80 | return 81 | case v := <-valueStream: 82 | if !fn(v) { 83 | return 84 | } 85 | takeStream <- v 86 | } 87 | } 88 | }() 89 | return takeStream 90 | } 91 | 92 | func skipN[T any](done <-chan struct{}, valueStream <-chan T, num int) <-chan T { 93 | takeStream := make(chan T) 94 | go func() { 95 | defer close(takeStream) 96 | for i := 0; i < num; i++ { 97 | select { 98 | case <-done: 99 | return 100 | case <-valueStream: 101 | } 102 | } 103 | for { 104 | select { 105 | case <-done: 106 | return 107 | case takeStream <- <-valueStream: 108 | } 109 | } 110 | }() 111 | 112 | return takeStream 113 | } 114 | 115 | func skipFn[T any](done <-chan struct{}, valueStream <-chan T, fn func(any) bool) <-chan T { 116 | takeStream := make(chan T) 117 | go func() { 118 | defer close(takeStream) 119 | for { 120 | select { 121 | case <-done: 122 | return 123 | case v := <-valueStream: 124 | if !fn(v) { 125 | takeStream <- v 126 | } 127 | } 128 | } 129 | }() 130 | return takeStream 131 | } 132 | 133 | func skipWhile[T any](done <-chan struct{}, valueStream <-chan T, fn func(any) bool) <-chan T { 134 | takeStream := make(chan T) 135 | go func() { 136 | defer close(takeStream) 137 | take := false 138 | for { 139 | select { 140 | case <-done: 141 | return 142 | case v := <-valueStream: 143 | if !take { 144 | take = !fn(v) 145 | if !take { 146 | continue 147 | } 148 | } 149 | takeStream <- v 150 | } 151 | } 152 | }() 153 | return takeStream 154 | } 155 | 156 | func main() { 157 | fmt.Println("asStream:") 158 | done := make(chan struct{}) 159 | ch := asStream(done, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 160 | for v := range ch { 161 | fmt.Println(v) 162 | } 163 | 164 | fmt.Println("asRepeatStream:") 165 | done = make(chan struct{}) 166 | ch = asRepeatStream(done, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 167 | i := 0 168 | for v := range ch { 169 | fmt.Println(v) 170 | i++ 171 | if i == 20 { 172 | break 173 | } 174 | } 175 | 176 | fmt.Println("takeN:") 177 | done = make(chan struct{}) 178 | for v := range takeN(done, asRepeatStream(done, 1, 2, 3), 6) { 179 | fmt.Println(v) 180 | } 181 | 182 | evenFn := func(v any) bool { 183 | return v.(int)%2 == 0 184 | } 185 | lessFn := func(v any) bool { 186 | return v.(int) < 3 187 | } 188 | 189 | fmt.Println("takeFn:") 190 | done = make(chan struct{}) 191 | i = 0 192 | for v := range takeFn(done, asRepeatStream(done, 1, 2, 3), evenFn) { 193 | fmt.Println(v) 194 | i++ 195 | if i == 20 { 196 | break 197 | } 198 | } 199 | 200 | fmt.Println("takeWhile:") 201 | done = make(chan struct{}) 202 | for v := range takeWhile(done, asRepeatStream(done, 1, 2, 3), lessFn) { 203 | fmt.Println(v) 204 | } 205 | 206 | fmt.Println("skipN:") 207 | done = make(chan struct{}) 208 | for v := range takeN(done, skipN(done, asRepeatStream(done, 1, 2, 3), 2), 4) { 209 | fmt.Println(v) 210 | } 211 | 212 | fmt.Println("skipFn:") 213 | done = make(chan struct{}) 214 | for v := range takeN(done, skipFn(done, asRepeatStream(done, 1, 2, 3), evenFn), 4) { 215 | fmt.Println(v) 216 | } 217 | 218 | fmt.Println("skipWhile:") 219 | done = make(chan struct{}) 220 | for v := range takeN(done, skipWhile(done, asRepeatStream(done, 1, 2, 3), lessFn), 4) { 221 | fmt.Println(v) 222 | } 223 | } 224 | --------------------------------------------------------------------------------