一、现状

GogoroutineGogoroutinegoroutine
ch := generate() 
go func() { 
        for range ch { } 
}()
generate()channelgoroutinechannelgoroutinechgenerate()goroutine
goroutineG-P-MGgoroutinegoroutine2-4kgoroutinegoroutine
func main()  {
	for i := 0; i < math.MaxInt64; i++ {
		go func(i int) {
			time.Sleep(5 * time.Second)
		}(i)
	}
}

如果不加以控制的话,直接崩溃

goroutine
1、Context
goroutineHTTP/RPCGoroutineRPCgoroutineContextgoroutine
Context
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context
goroutineWithCancel
package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	go Speak(ctx)
	time.Sleep(10 * time.Second)
	cancel()
	time.Sleep(2 * time.Second)
	fmt.Println("拜拜!")
}

func Speak(ctx context.Context) {
	for range time.Tick(time.Second) {
		select {
		case <-ctx.Done():
			fmt.Println("拜拜!")
			return
		default:
			fmt.Println("开始执行")
		}
	}
}

运行结果:

PS D:\cpz\go-demo\test13> go run test4.go
开始执行
开始执行
开始执行
开始执行
开始执行
开始执行
开始执行
开始执行
开始执行
拜拜!
拜拜!
withCancelBackgroundctxgoroutine1s10sgoroutinegoroutine
2、channel
channelgoroutineGogoroutine+channelcontextgoroutinecontextchannelchannel+selectchannel+closegoroutine
方式1:channel+select
package main

import "fmt"

func fibonacci(ch chan int, done chan struct{}) {
	x, y := 0, 1
	for {
		select {
		case ch <- x:
			x, y = y, x+y
		case <-done:
			fmt.Println("over")
			return
		}
	}
}
func main() {
	ch := make(chan int)
	done := make(chan struct{})
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(<-ch)
		}
		done <- struct{}{}
	}()
	fibonacci(ch, done)
}
channelchannelchannelselectchannelselectdefaultselect
ChannelChannelcaseChanneldefault
defaultnil channeldefault casenil channelselect
方式2:channel+close
package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int, 10)
	go func() {
		for i := 0; i < 10; i++ {
			ch <- i
		}
		close(ch)
	}()
	go func() {
		for val := range ch {
			fmt.Println(val)
		}
		fmt.Println("receive data over")
	}()
	time.Sleep(5 * time.Second)
	fmt.Println("program over")
}
channelfor-rangechannelrange chchannelgoroutine
goroutine
goroutineGogoroutinegoroutinegoroutinegoroutinegoroutine

0、常用的三方库

1、协程池

gogoroutinegoroutinegoroutineantsgo-playground/pooljeffail/tunnyants
package main

import (
	"fmt"
	ants "github.com/panjf2000/ants/v2"
	"sync"
	"sync/atomic"
	"time"
)

var sum int32

func myFunc(i interface{}) {
	n := i.(int32)
	atomic.AddInt32(&sum, n)
	fmt.Printf("run with %d\n", n)
}

func demoFunc() {
	time.Sleep(10 * time.Millisecond)
	fmt.Println("Hello World!")
}

func main() {
	defer ants.Release()

	runTimes := 1000

	// Use the common pool.
	var wg sync.WaitGroup
	syncCalculateSum := func() {
		demoFunc()
		wg.Done()
	}
	for i := 0; i < runTimes; i++ {
		wg.Add(1)
		_ = ants.Submit(syncCalculateSum)
	}
	wg.Wait()
	fmt.Printf("running goroutines: %d\n", ants.Running())
	fmt.Printf("finish all tasks.\n")

	// Use the pool with a function,
	// set 10 to the capacity of goroutine pool and 1 second for expired duration.
	p, _ := ants.NewPoolWithFunc(10, func(i interface{}) {
		myFunc(i)
		wg.Done()
	})
	defer p.Release()
	// Submit tasks one by one.
	for i := 0; i < runTimes; i++ {
		wg.Add(1)
		_ = p.Invoke(int32(i))
	}
	wg.Wait()
	fmt.Printf("running goroutines: %d\n", p.Running())
	fmt.Printf("finish all tasks, result is %d\n", sum)
}
ants.NewPoolWithFunc()goroutine10goroutinep.Invoke(data)antsgoroutinetaskFuncdata
Semaphore
Gogoroutine
package main

import (
	"context"
	"fmt"
	"golang.org/x/sync/semaphore"
	"runtime"
	"sync"
	"time"
)

const (
	// 同时运行的goroutine上限
	Limit = 5
	// 信号量的权重
	Weight = 1
)

func main() {
	start := time.Now()
	names := []int{
		1, 2, 3, 4, 5,
	}

	for i := 1; i <= 100000; i++ {
		names = append(names, i)
	}

	sem := semaphore.NewWeighted(Limit)
	var w sync.WaitGroup
	for _, name := range names {
		w.Add(1)
		go func(name int) {
			sem.Acquire(context.Background(), Weight)
			// ... 具体的业务逻辑

			fmt.Printf("Items is %v, This NumGoroutine is %v\n", name, runtime.NumGoroutine())
			sem.Release(Weight)
			w.Done()
		}(name)
	}
	w.Wait()
	times := time.Since(start)
	fmt.Println(times)
}
NewWeighted()goroutine3Acquiregoroutinereleasewaiters
3、channel+sync
waitGroupchannelgoroutinechannelgoroutine
package main

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

var (
	// channel长度
	poolCount = 5
	// 复用的goroutine数量
	goroutineCount = 10
)

func main() {
	start := time.Now()
	jobsChan := make(chan int, poolCount)

	// workers
	var wg sync.WaitGroup
	for i := 0; i < goroutineCount; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for item := range jobsChan {
				fmt.Printf("Items is %v, This NumGoroutine is %v\n", item, runtime.NumGoroutine())
			}
		}()
	}

	// senders
	for i := 0; i < 100000; i++ {
		jobsChan <- i
	}

	// 关闭channel,上游的goroutine在读完channel的内容,就会通过wg的done退出
	close(jobsChan)
	wg.Wait()

	times := time.Since(start)
	fmt.Println(times)
}
goroutine

四、总结

goroutinegoroutinegoroutinegoroutine