Go 语言入门指南:进阶与依赖管理(1)| 青训营

80 阅读3分钟

本文章将从工程实践角度,讲授在企业项目实际开发过程中的所遇的难题,重点讲解 Go 语言的进阶之路,以及在其依赖管理管理过程中如何演进。

GOroutine

Go语言提供了称为Goroutines的特殊功能。Goroutine是一种函数或方法,可与程序中存在的任何其他Goroutine一起独立且同时执行。换句话说,每个Go语言中同时执行的活动称为Goroutines,您可以将Goroutine视为轻量级线程。与线程相比,创建Goroutines的成本非常小。每个程序至少包含一个Goroutine,并且该Goroutine被称为主Goroutine。如果主Goroutine终止,则所有Goroutine在主Goroutine之下运行,那么程序中存在的所有goroutine也将终止;Goroutine始终在后台运行。

go中有协程和线程两个概念:

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

可通过在要调用的函数前面加上go关键字创建协程。

package main
import(
	"fmt"
)

func hello(str string){
	for i := 0; i < 5; i++{
		time.Sleep(1 * time.Second)
		fmt.Println(str,i)
	}
}

func main(){
	go hello("hello")
	
	hello("welcome")
}

程序中添加了Sleep()方法,它使主Goroutine在新Goroutine执行的1秒之间睡眠1秒,在屏幕上显示“hello”以及“welcome”,然后在1秒的主Goroutine重新调度并执行其操作后终止。

如果不使用Sleep方法,它只显示调用普通函数的结果,而不显示Goroutine的结果,因为执行新的Goroutine时,Goroutine调用会立即返回。它不像普通函数那样等待Goroutine完成执行,它们总是在Goroutine调用后一直前进到下一行,并忽略Goroutine返回的值。

在Go语言中,您还可以为匿名函数启动Goroutine,换句话说,您可以简单地通过使用go关键字作为该函数的前缀来创建匿名Goroutine,如以下语法所示:

//匿名函数调用
go func (parameter_list){
    // 语句
}(arguments)

协程之间若需要操作共享的变量,可通过并发锁lock实现数据同步,具体如下:

var (
	x    int64
	lock sync.Mutex  //为变量x加锁
)

func addL() {
	for i := 0; i < 2000; i++ {
		lock.Lock() //给协程上锁
		x += 1
		lock.Unlock() //解锁
	}
}

func add() {
	for i := 0; i < 2000; i++ {
		x += 1
	}
}

func main() {
	x = 0
	for i := 0; i < 5; i++ {
		go addL()
	}
	time.Sleep(time.Second)
	println(x)

	x = 0
	for i := 0; i < 5; i++ {
		go add()
	}
	time.Sleep(time.Second)
	println(x)
}

通道Channel

在Go语言中,通道是goroutine与另一个goroutine通信的媒介,并且这种通信是无锁的。换句话说,通道是一种技术,它允许一个goroutine将数据发送到另一个goroutine。默认情况下,通道是双向的,这意味着goroutine可以通过同一通道发送或接收数据。通道还分为有缓冲通道和无缓冲通道两种。

通道的发送、接收操作和关闭

Mychannel <- element //将内容发送到通道
element :=  <-Mychannel //从通道中接收数据,也可以直接将“=”后的看作是一个值
ele, ok:= <- Mychannel  
/*
在此,如果ok的值为true,则表示通道已打开,因此可以执行读取操作。
并且,如果的值为false,则表示该通道已关闭,因此将不执行读取操作。
*/

close(Mychannel) //关闭
func CalSquare(){
	//通道的声明
	//var src chan int
	src := make(chan int) //上面的简便写法

	//var dest chan int 3
	dest := make(chan int, 3)

	go func(){ //该子协程发送0-9数字
		defer close(src)
		for i := 0; i < 10; i++{
			src <- i   //将i发送给通道src
		}
	}()
	go func(){ //计算输入数字的平方
		defer close(dest)
		for i:= range src{
			dest <- i * i  //将i的平方发送给dest
		}
	}()
	for i := range dest{ //主协程 :输出最后的平方数
		println(i)
	}
}

WaitGroup

WaitGroup可以替代之前代码中的 time.Sleep() 方法,使得代码更优雅。

包含以下三个方法:

  • Add( delta int ) :计数器+delta
  • Done() : 计数器 - 1
  • Wait() : 主协程阻塞直到计数器为0
//快速打印hello 0-4
func hello(j int){
	println("hello", j)
}

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