GO语言并发 | 青训营笔记

39 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

协程

协程:用户态,轻量级线程,栈MB级别 线程:内核态,线程跑多个协程,栈KB级别

优势

1.消耗小:每个2kb内存,可以轻松创建大量的goroutine 2.启动时间快于线程 3.原生支持通过channel进行通信,go推荐使用通信来并发而不是内存共享,不用操心锁和同步

启动协程

func print(sum int) {
	fmt.Println(sum)
}
func main() {

	for i := 1; i <= 10; i++ {
		go print(i)
	}
	time.Sleep(time.Second)
}

使用go关键字进行协程的启动

CSP

通过通信的方式来共享内存

Channel(缓冲)

无缓冲通道:make(chan 类型) 例:make(chan int) 有缓冲通道:make(chan 类型,大小) 例:make(chan int,2)

func print(sum int) {
	fmt.Printf("%v\n", sum)
}
func main() {
	src := make(chan int)
	dest := make(chan int, 3)
	go func() {
		defer close(src)
		for i := 0; i < 10; i++ {
			src <- i
		}
	}()
	go func() {
		defer close(dest)
		for i := range src {
			dest <- i * i
		}
	}()
	for i := range dest {
		print(i)
	}
}

通过使用Channel保证了并行时执行的顺序

如果不使用缓冲区,那么dest会阻塞到数字被拿走再继续执行,如果有缓存,那么直到缓冲区满才会被阻塞,再某些情况下,缓冲区的大小也是值得商榷的事情

并发安全Lock

var (
	x    int64
	lock sync.Mutex
)

func addlock() {
	for i := 0; i < 2000; i++ {
		lock.Lock()
		x += 1
		lock.Unlock()
	}
}
func add() {
	for i := 0; i < 2000; i++ {
		x += 1
	}
}
func main() {
	for i := 0; i < 5; i++ {
		go add()
	}
	time.Sleep(time.Second)
	fmt.Println(x)
	x = 0
	for i := 0; i < 5; i++ {
		go addlock()
	}
	time.Sleep(time.Second)
	fmt.Println(x)
}

如果使用共享内存的方法,那就有可能出现多个协程对一块内存区域进行修改,而导致修改丢失的问题,所以我们得使用Muter互斥锁进行解决

WaitGroup

GO语言还可以使用WaitGroup解决多个并发任务的同步

ADD(delta int) 计数器加delta Done() 计数器-1 Wait() 阻塞到计数器为0

func main() {
	var wg sync.WaitGroup
	wg.Add(5)
	for i := 0; i < 5; i++ {
		go func(j int) {
			defer wg.Done()
			fmt.Println(j)
		}(i)
	}
	wg.Wait()
}

##总结 这次的课程学习到了Go语言的并发操作,之后还需要更多的应用对相关的操作进行熟悉