Go 语言进阶 | 青训营笔记

54 阅读2分钟

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

Go 语言进阶


1.1并行vs并发

并发(concurrency)与并行(parallelism)不同。并行是让不同的代码片段同时在不同的物理处理器上执行。并行的关键是同时做很多事情,而并发是指同时管理很多事情,这些事情可能只做了一半就被暂停去做别的事情了(Golang 的并发通过切换多个线程达到减少物理处理器空闲等待的目的) 。在很多情况下,并发的效果比并行好,因为操作系统和硬件的总资源一般很少,但能支持系统同时做很多事情。

image.png 通过这个简单的例子我们能够看出在多个核上运行的效率明显要高效一些。

1.2 goroutine

在go语言中想要开一个协程是十分简单的。我们只需要在代码块中加入go关键字就能够进入到子线程中去运行。 例如如下的代码块:

func Hello(){  //定义一个函数
   fmt.Println("Hello world)
}
func main() {
   go Sello() //开启一个协程
   go Hello() //再开启一个协程
   time.Sleep(time.Second * 3)//等待协程执行
}

运行结果:

image.png 在小数据下我们很难看出它的高效性,但是在实现业务时,能够支持大概开1e4个协程,十分的高效。

在运行代码时Sleep等待协程执行结束是不太可能的,因为实际中我们无法判断协程何时结束,所以引入WaitGroup来监控协程的执行状态。通过Add()、Done()和Wait()对计数器进行加1、减1和阻塞等待操作。

package main

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

func SayHello() {
	fmt.Println("Hello world")
}

func main() {
	var wg sync.WaitGroup
	wg.Add(2) //代表计数器+2 
	go SayHello()               //开启一个协程
	go SayHello()               //再开启一个协程
	wg.Wait() // 当计数器为0时,结束协程
	
}

1.3 channel

在协程之间有时候需要进行通信,交换数据来得到下一步的运行条件。那么在go语言中是如何进行通信的呢? 在go语言建议使用的是channel。

1.3.1 channel的创建

image.png

1.3.2 channel的使用

以下代码实现了一个最经典的生产-消费模型 通过src生产的数字到dest进行消费

package concurrence

func CalSquare() {

	src := make(chan int) // 无缓冲的通信通道
	dest := make(chan int, 3) 有缓冲的通信通道

	go func() {
		defer close(dest)
		for i := range src {
			dest <- i * i
		}
	}()

	go func() {
		defer close(src)

		for i := 1; i <= 10; i++ {
			src <- i
		}
	}()

	for i := range dest {
		println(i)
	}

}

运行结果:

image.png

A协程发送1-10数字 B协程计算平方 主协程输出平方数 B协程带有3个单位的缓冲,所以在生产是不需要担心生产速度归快的问题。

未完。。。。。