GO语言入门-工程实践(1)| 青训营

72 阅读3分钟

01.语言进阶(并发编程)

goroutine

协程: 用户态,轻量级线程,栈KB级别

线程: 内核态,线程跑多个协程,栈MB级别

  • goroutine 是 Go 语言中并发执行的基本单位,它是一种用户态的轻量级线程。
  • 通过使用 go 关键字,可以在 Go 语言中创建 goroutine。使用匿名函数或有名函数作为 goroutine 的执行体。
 // 匿名函数
 go func() {
     // goroutine 执行的代码
 }()
 ​
 // 有名函数
 func test(){
   fmt.Printf("golang tutorial\n")
 }
 go test()
  • goroutine 的调度是由 Go 运行时进行管理的,它可以在多个线程上并发执行。
  • goroutine 的创建和销毁开销相对较小,可以快速启动和切换,因此适用于高并发的场景。

channel

  • 通道(channel)是用来传递数据的一种数据结构,在 goroutine 之间进行同步和通信。
  • 通道可以用于在两个或多个 goroutine 之间传递特定类型的值。
  • 使用 make 函数创建通道,可以指定通道的容量(缓冲通道)或不指定容量(无缓冲通道)。
  • 通道的发送操作使用 <- 操作符,接收操作也使用 <- 操作符。
  • 通道的发送和接收操作会阻塞,直到另一端准备好。这种机制可以实现 goroutine 之间的同步和数据传递。
 package main
 ​
 func CalSquare() {
     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 {  // 接收
         println(i)
     }
 }
 func main() {
     CalSquare()
 }
 ​

sync

  • 可以使用 sync.Mutex创建锁
 package main
 ​
 import (
     "sync"
     "time"
 )
 ​
 var (
     x    int64
     lock sync.Mutex
 )
 ​
 func addWithLock() {
     for i := 0; i < 2000; i++ {
         lock.Lock()
         x += 1
         lock.Unlock()
     }
 }
 ​
 func addWithoutLock() {
     for i := 0; i < 2000; i++ {
         x += 1
     }
 }
 ​
 func Add() {
     x = 0
     for i := 0; i < 5; i++ {
         go addWithoutLock()
     }
     time.Sleep(time.Second)
     println("WithoutLock:", x) // <=1000
 ​
     x = 0
     for i := 0; i < 5; i++ {
         go addWithLock()
     }
     time.Sleep(time.Second)
     println("WithLock:", x)  //==1000
 }
 ​
 func main() {
     Add()
 }
  • 协程间同步(sync.WaitGroup)
 package main
 ​
 import "sync"
 ​
 func hello(i int) {
     println("hello goroutine : ", i)
 }
 ​
 func ManyGoWait() {
     var wg sync.WaitGroup
     wg.Add(5)  // 计数器加5
     for i := 0; i < 5; i++ {
         go func(j int) {
             defer wg.Done()  // 计数器减1
             hello(j)
         }(i)
     }
     wg.Wait()  // 阻塞直到计数器为0
 }
 ​
 func main() {
     ManyGoWait()
 }
 ​

02. 依赖管理

go依赖管理经历了三个阶段GOPATH -> Go Vendor -> Go Module,现在主流使用Go Module

  1. GOPATH

    • GOPATH 是 Go 语言支持的一个环境变量,它指定了 Go 项目的工作区目录。

    • GOPATH 目录下包含三个主要文件夹:bin、pkg 和 src。

      文件夹作用
      bin项目编译的二进制文件
      pkg项目编译的中间产物
      src项目源码
    • 在 GOPATH 中,项目的代码直接依赖于 src 目录下的代码。

    • 使用 go get 命令可以方便地下载并安装最新版本的包到 src 目录下。

  2. Go Vendor

    • Go Vendor 是在 GOPATH 基础上引入的一种依赖管理方式。
    • 在项目目录下增加了 vendor 文件夹,所有依赖包以副本的形式存放在项目的 vendor 目录下。
    • 当项目存在 vendor 目录时,Go 会优先使用 vendor 目录下的依赖包,如果依赖包不存在,则会从 GOPATH 中寻找。
    • 使用 Go Vendor 可以解决多个项目需要同一个包依赖的冲突问题,每个项目引入一份依赖的副本。
  3. Go Module

    Go Module 是 Go 语言官方推出的依赖管理解决方案,它包含了三个重要的要素:配置文件、中心仓库和本地工具。

    要素工具
    配置文件,描述依赖go.mod
    中心仓库管理依赖库Proxy
    本地工具go get / go mod
    • go get 使用

       go get example.org/pkg +...
      

      后面跟不同的指令能实现不同的功能:

      指令功能
      @update默认
      @none删除依赖
      @v1.1.2下载指定tag版本,语义版本
      @23dfdd5下载特定的commit版本
      @master下载分支的最新commit
    • go mod 使用

      指令功能
      init初始化,创建go.mod文件
      download下载模块到本地缓存
      tidy增加需要的依赖,删除不需要的依赖