Go變數作用域精講及代碼實戰

来源:https://www.cnblogs.com/xfuture/p/18242545
-Advertisement-
Play Games

關註作者,復旦AI博士,分享AI領域與雲服務領域全維度開發技術。擁有10+年互聯網服務架構、AI產品研發經驗、團隊管理經驗,同濟本復旦碩博,復旦機器人智能實驗室成員,國家級大學生賽事評審專家,發表多篇SCI核心期刊學術論文,阿裡雲認證的資深架構師,項目管理專業人士,上億營收AI產品研發負責人。 精講 ...


關註作者,復旦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產品業務負責人。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • json-server 快速搭建REST API 伺服器 ★ 認識json-server 官方文檔參考 json-server 是一個非常流行的開源工具,用於快速搭建一個完整的 REST API 伺服器。它使用 JSON 文件作為數據源,通過簡單的配置即可模擬複雜的伺服器功能,非常適合前端開發者在沒 ...
  • 這篇文章介紹瞭如何在Vue 3項目中配置ESLint和Prettier以統一代碼風格,實現代碼規範性與可讀性的提升。通過設置規則、解決衝突、以及將配置融入持續集成流程和代碼審查過程,確保團隊協作時代碼風格的一致性,提升開發效率與項目維護性。 ...
  • 基於Vue-Cli創建 現在官方推薦使用 create-vue 來創建基於 Vite 的新項目(⚠️ Vue CLI 現已處於維護模式!) # 查看@vue/cli版本號,確保@vue/cli版本在4.5.0以上 vue --version # 沒有安裝@vue/cil或者版本不在4.5.0以上執行 ...
  • 寫在前面 這是PB案例學習筆記系列文章的第5篇,該系列文章適合具有一定PB基礎的讀者。 通過一個個由淺入深的編程實戰案例學習,提高編程技巧,以保證小伙伴們能應付公司的各種開發需求。 文章中設計到的源碼,小凡都上傳到了gitee代碼倉庫https://gitee.com/xiezhr/pb-proje ...
  • Cursor 是一個基於 Visual Studio Code(VS Code)技術構建的高級代碼編輯器,專為提高編程效率並更深度地整合 AI 功能而設計。它不僅繼承了 VS Code 的強大功能和用戶界面,還增加了專門針對 AI 支持的特色功能。Cursor 是 VS Code 的一個分支,這意味... ...
  • 1 maven介紹 1)為什麼使用maven Maven是一個強大的項目管理和構建工具,它能夠簡化Java項目的構建、依賴管理和發佈過程。以下是Maven的一些主要特點和功能: 項目結構管理:Maven採用約定優於配置的原則,提供了標準的項目結構模板,使得開發人員可以快速創建和維護項目。 依賴管理: ...
  • NumPy 中的簡單算術運算可以通過 `add`, `subtract`, `multiply`, `divide`, `power`, `mod`, `remainder` 等函數實現,這些函數支持條件運算,並接受 `where` 參數。例如,`add()` 實現加法,`subtract()` 表... ...
  • JSON 簡介 JSON(JavaScript Object Notation,JavaScript對象表示法)是一種輕量級的數據交換格式。它基於ECMAScript(歐洲電腦製造商協會制定的js規範)的一個子集,採用完全獨立於編程語言的文本格式來存儲和表示數據。簡潔和清晰的層次結構使得JSON成 ...
一周排行
    -Advertisement-
    Play Games
  • 通過WPF的按鈕、文本輸入框實現了一個簡單的SpinBox數字輸入用戶組件並可以通過數據綁定數值和步長。本文中介紹了通過Xaml代碼實現自定義組件的佈局,依賴屬性的定義和使用等知識點。 ...
  • 以前,我看到一個朋友在對一個系統做初始化的時候,通過一組魔幻般的按鍵,調出來一個隱藏的系統設置界面,這個界面在常規的菜單或者工具欄是看不到的,因為它是一個後臺設置的關鍵界面,不公開,同時避免常規用戶的誤操作,它是作為一個超級管理員的入口功能,這個是很不錯的思路。其實Winform做這樣的處理也是很容... ...
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他的程式每次關閉時就會自動崩潰,一直找不到原因讓我幫忙看一下怎麼回事,這位朋友應該是第二次找我了,分析了下 dump 還是挺經典的,拿出來給大家分享一下吧。 二:WinDbg 分析 1. 為什麼會崩潰 找崩潰原因比較簡單,用 !analyze -v 命 ...
  • 在一些報表模塊中,需要我們根據用戶操作的名稱,來動態根據人員姓名,更新報表的簽名圖片,也就是電子手寫簽名效果,本篇隨筆介紹一下使用FastReport報表動態更新人員簽名圖片。 ...
  • 最新內容優先發佈於個人博客:小虎技術分享站,隨後逐步搬運到博客園。 創作不易,如果覺得有用請在Github上為博主點亮一顆小星星吧! 博主開始學習編程於11年前,年少時還只會使用cin 和cout ,給單片機點點燈。那時候,類似async/await 和future/promise 模型的認知還不是 ...
  • 之前在阿裡雲ECS 99元/年的活動實例上搭建了一個測試用的MINIO服務,以前都是直接當基礎設施來使用的,這次準備自己學一下S3相容API相關的對象存儲開發,因此有了這個小工具。目前僅包含上傳功能,後續計劃開發一個類似圖床的對象存儲應用。 ...
  • 目錄簡介快速入門安裝 NuGet 包實體類User資料庫類DbFactory增刪改查InsertSelectUpdateDelete總結 簡介 NPoco 是 PetaPoco 的一個分支,具有一些額外的功能,截至現在 github 星數 839。NPoco 中文資料沒多少,我是被博客園群友推薦的, ...
  • 前言 前面使用 Admin.Core 的代碼生成器生成了通用代碼生成器的基礎模塊 分組,模板,項目,項目模型,項目欄位的基礎功能,本篇繼續完善,實現最核心的模板生成功能,並提供生成預覽及代碼文件壓縮下載 準備 首先清楚幾個模塊的關係,如何使用,簡單畫一個流程圖 前面完成了基礎的模板組,模板管理,項目 ...
  • 假設需要實現一個圖標和文本結合的按鈕 ,普通做法是 直接重寫該按鈕的模板; 如果想作為通用的呢? 兩種做法: 附加屬性 自定義控制項 推薦使用附加屬性的形式 第一種:附加屬性 創建Button的附加屬性 ButtonExtensions 1 public static class ButtonExte ...
  • 在C#中,委托是一種引用類型的數據類型,允許我們封裝方法的引用。通過使用委托,我們可以將方法作為參數傳遞給其他方法,或者將多個方法組合在一起,從而實現更靈活的編程模式。委托類似於函數指針,但提供了類型安全和垃圾回收等現代語言特性。 基本概念 定義委托 定義委托需要指定它所代表的方法的原型,包括返回類 ...