GO语言进阶与依赖管理

114 阅读3分钟

Go 语言的管道使用

基本概念

channel,通道,又译作管道,是GO中的重要数据类型,用于传递数据。通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。
通道必须使用 make 创建,有三种创建方式:

ch := make(chan int) //双向通道
ch := make(<-chan int) //只读通道
ch := make(chan <-int) //只写通道

因为基本概念在前面已经讲过, 所以一笔带过

无缓冲管道的作用

如果通道不带缓冲,就是同步操作,即发送方会阻塞直到接收方从通道中接收了值,这可以用于使主线程等待协程完成通信。我个人认为这样的管道一般不太利于接收和发送的效率, 但可以用来对 一个 go routine 中指定代码结束后才能执行另一些代码起到阻塞作用

package main

import (
	"fmt"
	"time"
)

func Hello(s string) {
	for i := 0; i < 5; i++ {
		time.Sleep(100 * time.Millisecond)
		fmt.Println(s, (i+1)*100)
	}
}
func Hello2(s string, ch chan int) {
	for i := 0; i < 5; i++ {
		time.Sleep(150 * time.Millisecond)
		fmt.Println(s, (i+1)*150)
	}
	ch <- 0
	close(ch)
}

func main() {
	ch := make(chan int)
	go Hello2("world", ch)
	Hello("hello")
	fmt.Println(<-ch) //关键语句,这句会阻塞,直到Hello2执行完毕
}

按上述的代码则 只有当 Hello2 这个协程 结束后, 向 ch 管道输入数字才会使整个 main 函数结束, 而反之没有管道则很有可能会导致 协程中的 Hello2 还没有执行完就导致程序退出 当然还有其他办法解决协程未执行完就退出的方法: WaitGroup也可以对协程是否执行完毕进行控制

func main() {
   g := sync.WaitGroup{}
   g.Add(1)
   go func() {
      for i := 0; i < 100; i++ {
         time.Sleep(1 * time.Second)
      }
      g.Done()
   }()
   g.Wait()
}

每次开启一个协程时对 GroupWait 返回的对象 g 进行 Add() 操作, 当对应的协程结束后执行 Done(), 则在想要等待协程执行结束的位置进行 Wait() 就可以实现

有缓冲通道

通道缓冲的形象解释:
无缓冲是同步的,例如 make(chan int) 有缓冲是异步的,例如 make(chan int, 1) 无缓冲通道会导致通道存消息和取消息是阻塞的, 每次存时只有取出后才能执行下一个操作, 而有缓冲通道可以直接放入缓冲区(如果还没满), 然后就可以继续接下来的操作了

通道的关闭

需要注意的是有缓冲通道在通道关闭后缓冲区的内容依然可以取出, 并不会丢失里面的内容, 但是如果写完后需要注意关闭管道防止发生读管道处一直阻塞产生死锁

select

Select语句使得一个 goroutine 可以等待多个通信操作。select中的每个case必须是一个通道操作。select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行

package main

import "fmt"

func fibonacci(c, quit chan int) {
	x, y := 0, 1
	for {
		select {
		case c <- x:
			x, y = y, x+y
		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}

func main() {
	c := make(chan int)
	quit := make(chan int)

	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(<-c)
		}
		quit <- 0
	}()
	fibonacci(c, quit)
}


go 依赖的三个阶段

GOPATH

需要配置环境变量 $GOPATH

GO Vendor

在项目目录下存放一个 vendor 的文件夹, 用来存放依赖包的副本 ($ProjectRoot/vendor) 所以只有在 vendor 下找不到才去 GOPATH 下找

GO Module (目前广泛应用的)

通过 go.mod 文件 管理依赖包版本 从 go1.11 引入, 从 go1.16默认开启

依赖管理的三要素:

  1. 配置文件,描述依赖: go.mod
  2. 中心仓库管理依赖库: Proxy
  3. 本地工具: go get/mod