• Go变量作用域精讲及代码实战


    关注作者,复旦AI博士,分享AI领域与云服务领域全维度开发技术。拥有10+年互联网服务架构、AI产品研发经验、团队管理经验,同济本复旦硕博,复旦机器人智能实验室成员,国家级大学生赛事评审专家,发表多篇SCI核心期刊学术论文,阿里云认证的资深架构师,项目管理专业人士,上亿营收AI产品研发负责人。


    精讲Go语言中局部作用域、全局作用域、块作用域、包作用域、函数作用域的定义、内存管理和并发使用,提供丰富示例,帮助读者编写高效、安全的代码。

    file

    1. 变量的作用域概述

    在编程中,变量的作用域(Scope)定义了变量在程序中的可见性和生命周期。理解变量的作用域对于编写健壮且可维护的代码至关重要。Go语言(简称Go)提供了几种不同的作用域类型,使得开发者可以灵活地控制变量的可见范围和生命周期。本章节将详细概述Go语言中变量的各种作用域,帮助读者更好地理解和应用这些概念。

    1.1 作用域的类型

    在Go语言中,主要有以下几种作用域类型:

    作用域类型 描述 示例
    局部作用域 变量在函数或代码块内部声明,仅在该函数或代码块内可见。 func main() { var x int = 10 }
    全局作用域 变量在包级别声明,在同一包内的所有文件中都可见。 var y int = 20
    块作用域 变量在代码块(例如循环或条件语句)内部声明,仅在该代码块内可见。 for i := 0; i < 10; i++ { var z int = i }
    函数作用域 函数内的变量,仅在函数体内可见。 func foo() { var a int = 30 }
    包作用域 包级别的变量声明,在整个包范围内可见。 package main; var b int = 40

    1.2 作用域的可见性和生命周期

    不同作用域类型决定了变量的可见性和生命周期:

    1. 局部作用域

      • 可见性:局部变量仅在声明它们的函数或代码块内可见。
      • 生命周期:局部变量的生命周期从它们被声明开始,到函数或代码块执行完毕为止。
    2. 全局作用域

      • 可见性:全局变量在同一包内的所有文件中都可见。
      • 生命周期:全局变量在程序启动时被分配内存,并在程序结束时释放。
    3. 块作用域

      • 可见性:块作用域的变量仅在相应的代码块内可见。
      • 生命周期:块作用域的变量从代码块开始执行到结束时结束。
    4. 函数作用域

      • 可见性:函数作用域的变量仅在函数体内可见。
      • 生命周期:函数作用域的变量从函数调用开始到函数返回时结束。
    5. 包作用域

      • 可见性:包作用域的变量在整个包范围内可见。
      • 生命周期:包作用域的变量在包被加载时初始化,并在程序结束时释放。

    1.3 作用域与内存管理

    不同作用域的变量在内存管理上也有所不同:

    • 局部变量:通常分配在栈上,函数或代码块执行完毕后自动释放。
    • 全局变量:通常分配在堆上,直到程序结束时才释放。
    • 块变量:与局部变量类似,通常分配在栈上,块执行完毕后释放。
    • 函数变量:类似于局部变量,在栈上分配并在函数结束后释放。
    • 包变量:与全局变量类似,通常在堆上分配,直到程序结束。

    1.4 作用域的实际应用

    理解不同作用域的应用场景对于编写高效代码至关重要:

    • 局部变量适用于临时存储和局部计算,避免全局变量的命名冲突。
    • 全局变量适用于跨函数共享数据,但要小心避免数据竞争和不必要的内存占用。
    • 块变量适用于循环和条件判断中的临时数据存储。
    • 函数变量适用于封装函数内部逻辑,保证变量的私有性和安全性。
    • 包变量适用于包内共享数据,实现模块化设计。

    通过合理使用不同作用域,开发者可以有效管理变量的生命周期和可见性,提高代码的可维护性和性能。

    1.5 作用域的常见问题与调试技巧

    处理变量作用域时,可能遇到以下常见问题:

    • 变量遮蔽:内层作用域的变量名与外层作用域相同,导致外层变量被遮蔽。
    • 作用域污染:不合理使用全局变量,导致命名冲突和意外修改。
    • 生命周期管理:误用局部变量和全局变量,导致内存泄漏或性能问题。

    调试技巧包括:

    • 使用调试器逐步检查变量的值和生命周期。
    • 利用编译器警告和错误信息,及时发现作用域问题。
    • 编写单元测试,验证不同作用域下变量的行为。

    2. 局部作用域

    局部作用域是指变量在函数或代码块内部声明,其作用范围仅限于该函数或代码块。理解局部作用域对于编写安全、高效且可维护的代码至关重要。在本章节中,我们将详细探讨局部作用域的定义、内存管理及在并发环境中的使用。

    2.1 局部作用域的定义

    局部变量是在函数或代码块内部声明的变量。它们只能在声明它们的作用范围内访问,离开该范围后,这些变量将不再可见。局部变量的作用域通常较小,生命周期也较短,这使得它们在使用时非常高效。

    1. 函数内部的局部变量

      • 这些变量在函数体内声明,仅在函数体内可见。它们的生命周期从函数调用开始,到函数返回时结束。
      • 示例:
      func main() {
          var x int = 10
          fmt.Println("x in main:", x) // 输出: x in main: 10
      }
      
    2. 代码块内部的局部变量

      • 这些变量在代码块(如条件语句、循环语句)内部声明,仅在该代码块内可见。它们的生命周期从代码块开始执行,到代码块结束时结束。
      • 示例:
      func main() {
          if true {
              var y int = 20
              fmt.Println("y in if block:", y) // 输出: y in if block: 20
          }
          // fmt.Println("y outside if block:", y) // 编译错误: y 未定义
      }
      
    3. 嵌套作用域

      • 局部作用域可以嵌套,一个函数或代码块内部可以包含多个嵌套的代码块,每个代码块都有自己的局部变量。
      • 示例:
      func main() {
          var x int = 10
          if x > 5 {
              var y int = 20
              if y > 15 {
                  var z int = 30
                  fmt.Println("z in nested if block:", z) // 输出: z in nested if block: 30
              }
              // fmt.Println("z outside nested if block:", z) // 编译错误: z 未定义
          }
          // fmt.Println("y outside if block:", y) // 编译错误: y 未定义
      }
      

    局部变量的优点

    1. 避免命名冲突:由于局部变量的作用范围有限,它们不会与全局变量或其他函数的局部变量发生命名冲突。
    2. 内存管理高效:局部变量通常分配在栈上,函数或代码块执行完毕后自动释放,内存管理非常高效。
    3. 代码可读性强:局部变量使得变量的作用范围明确,增强了代码的可读性和可维护性。

    2.2 内存管理

    局部变量通常分配在栈上。当函数或代码块执行完毕后,这些局部变量会被自动释放。这种内存管理方式使得局部变量的分配和释放非常高效。

    func calculate() int {
        var result int = 0
        for i := 0; i < 10; i++ {
            result += i
        }
        return result
    }
    
    func main() {
        sum := calculate()
        fmt.Println("Sum:", sum) // 输出: Sum: 45
    }
    

    calculate函数中,变量resulti都是局部变量,它们的内存分配在栈上。当calculate函数执行完毕后,这些变量会被自动释放。

    2.3 并发环境中的局部变量

    在Go语言中,并发编程是其一大特性。在并发环境中使用局部变量可以避免数据竞争,因为每个goroutine都有自己独立的栈空间,局部变量不会在不同的goroutine之间共享。

    package main
    
    import (
        "fmt"
        "sync"
    )
    
    func printNumber(wg *sync.WaitGroup, num int) {
        defer wg.Done()
        fmt.Println("Number:", num)
    }
    
    func main() {
        var wg sync.WaitGroup
    
        for i := 0; i < 5; i++ {
            wg.Add(1)
            go printNumber(&wg, i)
        }
    
        wg.Wait()
    }
    

    在上述示例中,每个printNumber函数调用都会在新的goroutine中执行,num作为局部变量不会在不同的goroutine之间共享,确保了并发执行的安全性。

    3. 全局作用域

    全局作用域指的是在包级别声明的变量,它们在同一包内的所有文件中都可见。全局变量的使用需要谨慎,因为它们的生命周期贯穿整个程序运行过程,如果管理不当,可能会导致命名冲突、数据竞争等问题。在本章节中,我们将详细探讨全局作用域的定义、内存管理及在并发环境中的使用。

    3.1 全局作用域的定义

    全局变量是在包级别声明的变量,这些变量在包内的所有文件中都可见,并且它们的生命周期从程序启动开始,到程序结束时结束。全局变量可以在包的任意位置声明,一般在包级别的开头声明。

    1. 包级别声明

      • 全局变量通常在包的开头声明,使得包内所有文件都可以访问这些变量。
      • 示例:
      package main
      
      import "fmt"
      
      var globalVar int = 100 // 全局变量
      
      func main() {
          fmt.Println("globalVar in main:", globalVar) // 输出: globalVar in main: 100
      }
      
    2. 跨文件访问

      • 全局变量可以在同一包内的不同文件中访问。这对于共享数据或状态信息非常有用。
      • 示例:
      // file1.go
      package main
      
      var sharedVar int = 200 // 全局变量
      
      // file2.go
      package main
      
      import "fmt"
      
      func printSharedVar() {
          fmt.Println("sharedVar in printSharedVar:", sharedVar) // 输出: sharedVar in printSharedVar: 200
      }
      
      func main() {
          printSharedVar()
      }
      

    全局变量的优点

    1. 跨文件共享数据:全局变量可以在包内的所有文件中共享数据或状态信息,方便模块化编程。
    2. 持久性:全局变量的生命周期贯穿程序运行始终,适用于需要持久存储的数据。

    3.2 内存管理

    全局变量通常分配在堆上。由于全局变量的生命周期从程序启动到程序结束,内存管理需要特别注意,确保没有不必要的内存占用。

    package main
    
    import "fmt"
    
    var counter int = 0 // 全局变量
    
    func increment() {
        counter++
    }
    
    func main() {
        for i := 0; i < 10; i++ {
            increment()
        }
        fmt.Println("Final counter value:", counter) // 输出: Final counter value: 10
    }
    

    在上述示例中,变量counter是全局变量,生命周期贯穿整个程序运行过程。当increment函数被调用时,counter的值会递增。

    3.3 并发环境中的全局变量

    在Go语言中,并发编程是其一大特性。全局变量在并发环境中需要特别小心,因为多个goroutine可能会同时访问和修改全局变量,从而导致数据竞争和不一致性。

    package main
    
    import (
        "fmt"
        "sync"
    )
    
    var counter int = 0 // 全局变量
    var mu sync.Mutex   // 互斥锁
    
    func increment(wg *sync.WaitGroup) {
        defer wg.Done()
        mu.Lock()   // 加锁
        counter++
        mu.Unlock() // 解锁
    }
    
    func main() {
        var wg sync.WaitGroup
    
        for i := 0; i < 10; i++ {
            wg.Add(1)
            go increment(&wg)
        }
    
        wg.Wait()
        fmt.Println("Final counter value:", counter) // 输出: Final counter value: 10
    }
    

    在上述示例中,counter是一个全局变量,为了在并发环境中安全地访问和修改它,我们使用了互斥锁(sync.Mutex)来避免数据竞争。

    4. 块作用域

    块作用域(Block Scope)是指在特定代码块(如条件语句、循环语句等)内部声明的变量,其作用范围仅限于该代码块。块作用域变量在声明它们的代码块外部不可见。理解块作用域对于编写高效且可维护的代码非常重要。在本章节中,我们将详细探讨块作用域的定义、内存管理及在不同代码结构中的使用。

    1. 块作用域的定义

    块作用域指的是变量在代码块内部声明,其作用范围仅限于该代码块。代码块可以是由大括号 {} 包围的一段代码,如函数、条件语句、循环语句等。块作用域变量的生命周期从代码块开始到代码块结束。

    1. 条件语句中的块作用域

      • 在条件语句(如 ifelse ifelse)内部声明的变量,其作用范围仅限于该条件语句块。
      • 示例:
      package main
      
      import "fmt"
      
      func main() {
          x := 10
          if x > 5 {
              y := 20
              fmt.Println("y in if block:", y) // 输出: y in if block: 20
          }
          // fmt.Println("y outside if block:", y) // 编译错误: y 未定义
      }
      
    2. 循环语句中的块作用域

      • 在循环语句(如 forrange)内部声明的变量,其作用范围仅限于该循环语句块。
      • 示例:
      package main
      
      import "fmt"
      
      func main() {
          for i := 0; i < 3; i++ {
              msg := "Iteration"
              fmt.Println(msg, i) // 输出: Iteration 0, Iteration 1, Iteration 2
          }
          // fmt.Println(msg) // 编译错误: msg 未定义
      }
      
    3. 嵌套块作用域

      • 块作用域可以嵌套,一个代码块内部可以包含多个嵌套的代码块,每个代码块都有自己的局部变量。
      • 示例:
      package main
      
      import "fmt"
      
      func main() {
          x := 10
          if x > 5 {
              y := 20
              if y > 15 {
                  z := 30
                  fmt.Println("z in nested if block:", z) // 输出: z in nested if block: 30
              }
              // fmt.Println("z outside nested if block:", z) // 编译错误: z 未定义
          }
          // fmt.Println("y outside if block:", y) // 编译错误: y 未定义
      }
      

    块作用域的优点

    1. 避免命名冲突:由于块作用域变量的作用范围有限,它们不会与其他块或函数的变量发生命名冲突。
    2. 内存管理高效:块作用域变量通常分配在栈上,代码块执行完毕后自动释放,内存管理非常高效。
    3. 代码可读性强:块作用域使得变量的作用范围明确,增强了代码的可读性和可维护性。

    2. 内存管理

    块作用域变量通常分配在栈上。当代码块执行完毕后,这些变量会被自动释放。这种内存管理方式使得块作用域变量的分配和释放非常高效。

    package main
    
    import "fmt"
    
    func calculateSum() int {
        sum := 0
        for i := 1; i <= 10; i++ {
            sum += i
        }
        return sum
    }
    
    func main() {
        result := calculateSum()
        fmt.Println("Sum:", result) // 输出: Sum: 55
    }
    

    在上述示例中,变量 sumi 都是在 for 循环语句块内部声明的块作用域变量,它们的内存分配在栈上,for 循环执行完毕后,这些变量会被自动释放。

    3. 块作用域在不同代码结构中的使用

    块作用域在条件语句中非常有用,因为它们可以限制变量的作用范围,使得变量只在条件成立时存在。

    package main
    
    import "fmt"
    
    func main() {
        x := 5
        if x < 10 {
            message := "x is less than 10"
            fmt.Println(message) // 输出: x is less than 10
        } else {
            message := "x is 10 or more"
            fmt.Println(message)
        }
        // fmt.Println(message) // 编译错误: message 未定义
    }
    

    在上述示例中,变量 messageifelse 块中分别声明,具有各自独立的作用域。

    **循环语句中的块作用域

    在循环语句中使用块作用域变量,可以确保每次迭代都有独立的变量实例,避免变量状态被意外修改。

    package main
    
    import "fmt"
    
    func main() {
        for i := 0; i < 5; i++ {
            count := i * 2
            fmt.Println("Count:", count) // 输出: Count: 0, 2, 4, 6, 8
        }
        // fmt.Println("Count outside loop:", count) // 编译错误: count 未定义
    }
    

    在上述示例中,变量 countfor 循环的每次迭代中声明,并且每次迭代都是一个新的实例。

    **嵌套代码块中的块作用域

    使用嵌套代码块可以有效地管理变量的作用范围,避免变量的命名冲突。

    package main
    
    import "fmt"
    
    func main() {
        total := 0
        for i := 1; i <= 3; i++ {
            partial := i * 10
            {
                temp := partial + 5
                fmt.Println("Temp:", temp) // 输出: Temp: 15, 25, 35
            }
            // fmt.Println("Temp outside nested block:", temp) // 编译错误: temp 未定义
        }
    }
    

    在上述示例中,变量 temp 仅在嵌套的代码块内可见,离开该块后即不可见。

    5. 包作用域

    包作用域(Package Scope)是指变量在包级别声明,其作用范围覆盖整个包,即同一个包中的所有文件都可以访问这些变量。包作用域在Go语言中非常重要,因为它有助于实现模块化编程和代码的可维护性。在本章节中,我们将详细探讨包作用域的定义、内存管理及其在不同代码结构中的使用。

    5.1 包作用域的定义

    包作用域变量是在包级别声明的,这些变量在同一个包中的所有文件中都可见。包作用域变量的生命周期从包被加载开始,到程序结束时结束。通常,包作用域变量在包的顶层声明。

    1. 包级别声明

      • 包作用域变量通常在包的开头或文件的最顶层声明,使得包内所有文件都可以访问这些变量。
      • 示例:
      package main
      
      import "fmt"
      
      var packageVar int = 100 // 包作用域变量
      
      func main() {
          fmt.Println("packageVar in main:", packageVar) // 输出: packageVar in main: 100
      }
      
    2. 跨文件访问

      • 包作用域变量可以在同一个包内的不同文件中访问,这对于共享数据或状态信息非常有用。
      • 示例:
      // file1.go
      package main
      
      var sharedVar int = 200 // 包作用域变量
      
      // file2.go
      package main
      
      import "fmt"
      
      func printSharedVar() {
          fmt.Println("sharedVar in printSharedVar:", sharedVar) // 输出: sharedVar in printSharedVar: 200
      }
      
      func main() {
          printSharedVar()
      }
      

    包作用域的优点

    1. 跨文件共享数据:包作用域变量可以在包内的所有文件中共享数据或状态信息,方便模块化编程。
    2. 持久性:包作用域变量的生命周期从包加载到程序结束,适用于需要持久存储的数据。

    5.2 内存管理

    包作用域变量通常分配在堆上。由于包作用域变量的生命周期从程序启动到程序结束,内存管理需要特别注意,确保没有不必要的内存占用。

    package main
    
    import "fmt"
    
    var counter int = 0 // 包作用域变量
    
    func increment() {
        counter++
    }
    
    func main() {
        for i := 0; i < 10; i++ {
            increment()
        }
        fmt.Println("Final counter value:", counter) // 输出: Final counter value: 10
    }
    

    在上述示例中,变量counter是包作用域变量,其生命周期贯穿整个程序运行过程。当increment函数被调用时,counter的值会递增。

    5.3 包作用域在不同代码结构中的使用

    模块化编程中的包作用域

    包作用域在模块化编程中非常重要,它可以将相关的功能和数据封装在一个包中,实现高内聚、低耦合的设计。

    // config.go
    package config
    
    var AppName string = "MyApp" // 包作用域变量
    var Version string = "1.0"
    
    // main.go
    package main
    
    import (
        "fmt"
        "config"
    )
    
    func main() {
        fmt.Println("App Name:", config.AppName) // 输出: App Name: MyApp
        fmt.Println("Version:", config.Version)  // 输出: Version: 1.0
    }
    

    在上述示例中,config包中的变量AppNameVersion具有包作用域,可以在main包中访问,从而实现配置的集中管理。

    包作用域与初始化函数

    包作用域变量可以与初始化函数(init函数)结合使用,在程序开始时进行必要的初始化操作。

    package main
    
    import "fmt"
    
    var configVar string
    
    func init() {
        configVar = "Initialized" // 初始化包作用域变量
    }
    
    func main() {
        fmt.Println("configVar:", configVar) // 输出: configVar: Initialized
    }
    

    在上述示例中,init函数在程序启动时自动执行,对包作用域变量configVar进行初始化。

    包作用域与并发编程

    在并发编程中,包作用域变量需要特别小心,因为多个goroutine可能会同时访问和修改包作用域变量,从而导致数据竞争和不一致性。

    package main
    
    import (
        "fmt"
        "sync"
    )
    
    var counter int = 0 // 包作用域变量
    var mu sync.Mutex   // 互斥锁
    
    func increment(wg *sync.WaitGroup) {
        defer wg.Done()
        mu.Lock()   // 加锁
        counter++
        mu.Unlock() // 解锁
    }
    
    func main() {
        var wg sync.WaitGroup
    
        for i := 0; i < 10; i++ {
            wg.Add(1)
            go increment(&wg)
        }
    
        wg.Wait()
        fmt.Println("Final counter value:", counter) // 输出: Final counter value: 10
    }
    

    在上述示例中,counter是一个包作用域变量,为了在并发环境中安全地访问和修改它,我们使用了互斥锁(sync.Mutex)来避免数据竞争。

    6. 函数作用域

    函数作用域(Function Scope)指的是在函数内部声明的变量,其作用范围仅限于该函数。这些变量在函数外部不可见,离开函数后即被销毁。函数作用域在Go语言中非常重要,因为它可以有效地管理变量的生命周期,避免命名冲突和内存泄漏。在本章节中,我们将详细探讨函数作用域的定义、内存管理及其在不同代码结构中的使用。

    6.1 函数作用域的定义

    函数作用域是指在函数内部声明的变量,这些变量只能在该函数内部访问,函数执行结束后,这些变量就会被销毁。函数作用域的变量包括函数参数、局部变量以及在函数内部声明的任何其他变量。

    1. 函数内部声明的变量

      • 这些变量只能在声明它们的函数内部访问,生命周期从函数调用开始,到函数返回结束。
      • 示例:
      package main
      
      import "fmt"
      
      func calculate(a int, b int) int {
          sum := a + b // sum 是函数作用域变量
          return sum
      }
      
      func main() {
          result := calculate(3, 4)
          fmt.Println("Result:", result) // 输出: Result: 7
      }
      
    2. 函数参数

      • 函数参数也是函数作用域的一部分,它们在函数调用时被传递,在函数内部使用。
      • 示例:
      package main
      
      import "fmt"
      
      func greet(name string) {
          message := "Hello, " + name // name 是函数参数,具有函数作用域
          fmt.Println(message)
      }
      
      func main() {
          greet("Alice") // 输出: Hello, Alice
      }
      

    函数作用域的优点

    1. 避免命名冲突:由于函数作用域变量的作用范围仅限于函数内部,它们不会与其他函数的变量发生命名冲突。
    2. 内存管理高效:函数作用域变量通常分配在栈上,函数执行完毕后自动释放,内存管理非常高效。
    3. 代码可读性强:函数作用域使得变量的作用范围明确,增强了代码的可读性和可维护性。

    6.2 内存管理

    函数作用域变量通常分配在栈上。当函数执行完毕后,这些变量会被自动释放。这种内存管理方式使得函数作用域变量的分配和释放非常高效。

    内存分配示例

    package main
    
    import "fmt"
    
    func factorial(n int) int {
        if n == 0 {
            return 1
        }
        return n * factorial(n-1)
    }
    
    func main() {
        result := factorial(5)
        fmt.Println("Factorial:", result) // 输出: Factorial: 120
    }
    

    在上述示例中,n 是函数 factorial 的参数,其内存分配在栈上,函数执行完毕后自动释放。

    6.3 函数作用域在不同代码结构中的使用

    嵌套函数中的函数作用域

    Go语言支持在一个函数内部声明另一个函数,这使得函数作用域可以嵌套使用。

    package main
    
    import "fmt"
    
    func outerFunction() {
        outerVar := "I am outside!"
        
        func innerFunction() {
            innerVar := "I am inside!"
            fmt.Println(outerVar) // 输出: I am outside!
            fmt.Println(innerVar) // 输出: I am inside!
        }
    
        innerFunction()
        // fmt.Println(innerVar) // 编译错误: innerVar 未定义
    }
    
    func main() {
        outerFunction()
    }
    

    在上述示例中,innerFunction 是在 outerFunction 内部声明的嵌套函数。outerVarouterFunction 的局部变量,但在 innerFunction 中可见,而 innerVar 仅在 innerFunction 内部可见。

    闭包中的函数作用域

    闭包是指在其词法作用域内引用了自由变量的函数。Go语言中的闭包可以捕获并记住其外层函数中的变量。

    package main
    
    import "fmt"
    
    func adder() func(int) int {
        sum := 0
        return func(x int) int {
            sum += x
            return sum
        }
    }
    
    func main() {
        pos, neg := adder(), adder()
        for i := 0; i < 10; i++ {
            fmt.Println(pos(i))  // 累加正数
            fmt.Println(neg(-2*i)) // 累加负数
        }
    }
    

    在上述示例中,adder 函数返回一个闭包,该闭包捕获了外层函数的变量 sum,并在多次调用中累加 sum 的值。

    6.4 函数作用域与并发编程

    在并发编程中,函数作用域变量对于保证数据安全和避免数据竞争非常重要。每个 goroutine 都有自己的函数作用域,因此函数内部的局部变量在不同的 goroutine 之间不会共享。

    package main
    
    import (
        "fmt"
        "sync"
    )
    
    func printNumbers(wg *sync.WaitGroup, start int) {
        defer wg.Done()
        for i := start; i < start+5; i++ {
            fmt.Println(i)
        }
    }
    
    func main() {
        var wg sync.WaitGroup
    
        for i := 0; i < 3; i++ {
            wg.Add(1)
            go printNumbers(&wg, i*10)
        }
    
        wg.Wait()
    }
    

    在上述示例中,每个 printNumbers 函数调用在不同的 goroutine 中执行,且 istart 变量均具有函数作用域,保证了并发执行的安全性。

    如有帮助,请多关注
    TeahLead KrisChang,10+年的互联网和人工智能从业经验,10年+技术和业务团队管理经验,同济软件工程本科,复旦工程管理硕士,阿里云认证云服务资深架构师,上亿营收AI产品业务负责人。

  • 相关阅读:
    TensorRt(2)快速入门介绍
    基于微信小程序的图书馆选座系统源码
    xshell报错-要继续使用此程序,您必须应用最新的更新或使用新版本
    Ubuntu部署docker及docker常用操作
    【打卡】牛客网:BM58 字符串的排列
    LeetCode 2731. 移动机器人【脑筋急转弯,排序,前缀和】1922
    【数据结构-队列 二】【单调队列】滑动窗口最大值
    position left设置居中,除了auto以外,还有什么方式
    python 高级技巧
    21.9 Python 使用Selenium库
  • 原文地址:https://www.cnblogs.com/xfuture/p/18242545