ZetCode

Golang close 函数

最后修改时间 2025 年 5 月 8 日

本教程将解释如何在 Go 中使用内置的 close 函数。我们将通过实际的通道管理示例来介绍通道基础知识。

close 函数用于关闭 Go 中的通道。关闭通道表示不再向其发送任何值。这对于正确的通道清理和防止 goroutine 泄露很重要。

在 Go 中,close 与通道一起使用,以指示发送操作的完成。已关闭的通道在变空之前仍可从中读取,但向已关闭的通道发送会导致 panic。

基本的通道关闭示例

close 的最简单用法是在发送完所有值后关闭通道。本示例演示了基本的通道关闭。
注意:只有发送方应该关闭通道,接收方永远不应该。

basic_close.go
package main

import "fmt"

func main() {
    ch := make(chan int, 3)
    
    ch <- 1
    ch <- 2
    ch <- 3
    
    close(ch) // Close the channel after sending values
    
    for num := range ch {
        fmt.Println("Received:", num)
    }
    
    fmt.Println("Channel closed and drained")
}

在发送三个值后,通道被关闭。range 循环会一直读取所有值,直到通道变空并关闭。

检测通道关闭

我们可以使用接收操作的第二个返回值来检测通道何时关闭。本示例显示了显式的通道关闭检测。

detect_close.go
package main

import "fmt"

func main() {
    ch := make(chan string, 2)
    
    ch <- "first"
    ch <- "second"
    close(ch)
    
    for {
        msg, ok := <-ch
        if !ok {
            fmt.Println("Channel closed")
            break
        }
        fmt.Println("Received:", msg)
    }
}

当通道关闭且变空时,ok 布尔值将变为 false。这种模式对于显式的通道关闭检测非常有用。

使用 goroutine 关闭通道

正确使用 goroutine 关闭通道可以防止 goroutine 泄露。本示例展示了 goroutine 之间协调的通道关闭。

goroutine_close.go
package main

import (
    "fmt"
    "time"
)

func worker(tasks <-chan int, done chan<- bool) {
    for task := range tasks {
        fmt.Println("Processing task", task)
        time.Sleep(500 * time.Millisecond)
    }
    done <- true
}

func main() {
    tasks := make(chan int, 3)
    done := make(chan bool)
    
    go worker(tasks, done)
    
    for i := 1; i <= 3; i++ {
        tasks <- i
    }
    close(tasks) // Signal no more tasks
    
    <-done // Wait for worker to finish
    fmt.Println("All tasks processed")
}

主 goroutine 在发送完毕后关闭 tasks 通道。worker goroutine 在检测到通道关闭时退出。

关闭多个接收者

关闭一个通道可以同时通知多个接收者 goroutine。本示例通过通道关闭演示了广播。

multiple_receivers.go
package main

import (
    "fmt"
    "sync"
)

func receiver(id int, ch <-chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    for num := range ch {
        fmt.Printf("Receiver %d got %d\n", id, num)
    }
    fmt.Printf("Receiver %d exiting\n", id)
}

func main() {
    var wg sync.WaitGroup
    ch := make(chan int)
    
    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go receiver(i, ch, &wg)
    }
    
    for i := 1; i <= 5; i++ {
        ch <- i
    }
    close(ch) // Signal all receivers to exit
    
    wg.Wait()
    fmt.Println("All receivers exited")
}

关闭通道会导致所有三个接收者 goroutine 退出。WaitGroup 确保了正确的同步。

防止因关闭的通道引起的 panic

向已关闭的通道发送会导致 panic。本示例展示了如何安全地处理通道操作以防止 panic。

safe_operations.go
package main

import (
    "fmt"
    "sync"
)

func safeSend(ch chan<- int, value int, wg *sync.WaitGroup) {
    defer wg.Done()
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()
    
    ch <- value
}

func main() {
    var wg sync.WaitGroup
    ch := make(chan int, 1)
    
    // First send works
    wg.Add(1)
    go safeSend(ch, 1, &wg)
    
    // Close the channel
    close(ch)
    
    // Second send would panic but we recover
    wg.Add(1)
    go safeSend(ch, 2, &wg)
    
    wg.Wait()
    fmt.Println("Program completed safely")
}

safeSend 函数使用 recover 来处理潜在的 panic。这种模式使得在复杂系统中进行通道操作更加健壮。

来源

Go 语言规范

本教程通过实际的通道管理和清理示例,涵盖了 Go 中的 close 函数。

作者

我的名字是 Jan Bodnar,我是一位充满热情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。到目前为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有超过十年的经验。

列出所有 Golang 教程