并发

goroutine 不是os线程、不是绿色线程(由语言运行时管理的线程),是协程。协程是一种非抢占式的简单并发子goroutine(函数、闭包或方法),也就是说,它们不能被中断。取而代之的是,协程有多个点,允许暂停或重新进入 —Go语言并发之道

goroutinechannelsgoroutine

基础知识

goroutine

goroutine是go

channel 通道

通道单向通道无缓冲的通道带缓冲的通道同步信号(shutdown/close/finish)消息传递(queue/stream)互斥(mutex)
//go中对chan的定义   /runtime/chan.go
type hchan struct {
	qcount   uint           // total data in the queue    数据总量
	dataqsiz uint           // size of the circular queue
	buf      unsafe.Pointer // points to an array of dataqsiz elements
	elemsize uint16
	closed   uint32      //标记channel已经关闭
	elemtype *_type // element type
	sendx    uint   // send index
	recvx    uint   // receive index
	recvq    waitq  // list of recv waiters    和ring buffer相关
	sendq    waitq  // list of send waiters    和ring buffer相关

	// lock protects all fields in hchan, as well as several
	// fields in sudogs blocked on this channel.
	//
	// Do not change another G's status while holding this lock
	// (in particular, do not ready a G), as this can deadlock
	// with stack shrinking.
	lock mutex    //保护所有数据结构,但在某些quick path场景下不必加锁
}

type waitq struct {
	first *sudog
	last  *sudog     //sudog dequeue中等待的goroutine以及他的数据存储
}
  • 任何时间只能有一个goroutine访问chan
  • chan 里面的数据满足FIFO规则
//声明通道
var 通道变量  chan  通道类型
//创建通道
通道实例  :=  make(chan 通道类型)
//给通道发送数据
通道变量  <-值
//读取通道数据
data, ok := <-通道变量

defer关键字

defer

go调度模型

goroutine系统线程Processor调度实体

实现方式

syn包

进行多个任务的同步,主要操作有Add()、Done()、Wait()等操作

  • 互斥锁和读写锁
sync.Mutexsync.RWMutex

使用注意事项:

1、配套使用Lock、UnLock
2、运行时离开当前逻辑就释放锁
3、锁的粒度越小越好,加锁后尽快释放锁
4、没有特殊原因,尽量不要defer释放锁
5、RWMutex的读锁不要嵌套使用

  • List item
  • cond
sync.CondBroadcast()Signal()Wait()L LockBroadcast()Signal()Wait()
  • once
sync.Once
func main() {
	var count int
	increment := func() {
		count++
	}

	var once sync.Once
	var increments sync.WaitGroup
	increments.Add(100)
	for i:=0;i<100;i++{
		go func() {
			defer increments.Done()
			once.Do(increment)
		}()
	}

	increments.Wait()
	fmt.Printf("count is %d\n", count)
}
//最终输出为 1

池(pool)是Pool模式的并发安全实现,需要参照pool设计模式

channel

select 语句

GOMAXPROCS控制

runtime.GOMAXPROCS(runtime.NumCPU())   //充分利用cpu资源
Go语言的并发通过
channelgoroutineCSP(Communicating Sequential Process)

经典例子分析

case1

func handler(){
	ch := make(chan string)
	go func() {
		time.Sleep(3*time.Second)
		ch <- "job result"
	}()

	select {
	case result := <- ch:
		fmt.Println(result)
	case <-time.After(time.Second):
		return
	}
}

上述代码会造成goroutine泄漏,原因在于channel没有缓存
可能造成goroutine泄漏的原因有:
1、channel没有缓存
2、select命中timeout逻辑
3、导致channel没有消费者
4、最终导致anonymous goroutine泄漏

case2


var mu sync.RWMutex
func main() {
	go A()
	time.Sleep(2*time.Second)

	fmt.Println("main call lock")
	mu.Lock()
	defer mu.Unlock()
}

func A()  {
	fmt.Println("A call rlock")
	mu.RLock()
	fmt.Println("A rlocked")
	defer mu.RUnlock()
	B()
}

func B(){
	time.Sleep(5*time.Second)
	C()
}

func C()  {
	fmt.Println("C call rlock")
	mu.RLock()
	fmt.Println("C rlocked")
	mu.RUnlock()
}

运行结果如下:
在这里插入图片描述

谨慎使用锁的递归调用,上面的还需再看一看

case3

map
gomap
  • hash算法:AES
  • 冲突解决:链地址法(和java类似) Python采用开放定址法
  • range go每次rangemap的时候顺序都会不同,因为go故意实现了random 【需要代码论据】
  • 装填因子 6.5
  • rehash 渐进式rehash (和redis类似)

Semaphore信号量

自旋锁