本文章将从工程实践角度,讲授在企业项目实际开发过程中的所遇的难题,重点讲解 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()
}