Channel通道

Channel基础

Channel是Go语言中用于goroutine之间通信的管道。本节介绍Channel的基本概念和使用方法。

package main

import (
    "fmt"
    "time"
)

func main() {
    // 创建一个无缓冲的channel
    ch := make(chan string)
    
    // 启动一个goroutine发送数据
    go func() {
        ch <- "Hello from goroutine!"
    }()
    
    // 主goroutine接收数据
    msg := <-ch
    fmt.Println(msg)
    
    // 创建一个带缓冲的channel
    bufferedCh := make(chan int, 3)
    
    // 发送数据到缓冲channel
    bufferedCh <- 1
    bufferedCh <- 2
    bufferedCh <- 3
    
    // 接收数据
    fmt.Println(<-bufferedCh) // 1
    fmt.Println(<-bufferedCh) // 2
    fmt.Println(<-bufferedCh) // 3
}

Channel操作

Channel支持发送、接收、关闭等基本操作,本节展示这些操作的使用方法。

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int)
    
    // 发送操作
    go func() {
        for i := 0; i < 5; i++ {
            ch <- i
            time.Sleep(time.Millisecond * 100)
        }
        close(ch) // 关闭channel
    }()
    
    // 接收操作
    for {
        if val, ok := <-ch; ok {
            fmt.Println("Received:", val)
        } else {
            fmt.Println("Channel closed")
            break
        }
    }
    
    // 使用range接收
    ch2 := make(chan string)
    go func() {
        ch2 <- "Hello"
        ch2 <- "World"
        close(ch2)
    }()
    
    for msg := range ch2 {
        fmt.Println(msg)
    }
}

Select语句

Select语句用于在多个channel操作中进行选择,实现非阻塞的通信。

package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    go func() {
        time.Sleep(time.Second)
        ch1 <- "from ch1"
    }()
    
    go func() {
        time.Sleep(time.Second * 2)
        ch2 <- "from ch2"
    }()
    
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println(msg1)
        case msg2 := <-ch2:
            fmt.Println(msg2)
        case <-time.After(time.Second * 3):
            fmt.Println("timeout")
        }
    }
}

Channel方向

Channel可以是单向的(只读或只写),用于限制channel的使用方式。

package main

import "fmt"

// 只写channel
func sendData(ch chan<- int) {
    for i := 0; i < 5; i++ {
        ch <- i
    }
    close(ch)
}

// 只读channel
func receiveData(ch <-chan int) {
    for val := range ch {
        fmt.Println("Received:", val)
    }
}

func main() {
    ch := make(chan int)
    go sendData(ch)
    receiveData(ch)
}

Channel超时控制

使用select和time.After实现channel操作的超时控制。

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan string)
    
    // 模拟耗时操作
    go func() {
        time.Sleep(time.Second * 2)
        ch <- "operation completed"
    }()
    
    // 设置超时
    select {
    case result := <-ch:
        fmt.Println(result)
    case <-time.After(time.Second):
        fmt.Println("operation timed out")
    }
}

Channel与Context

结合Context包实现channel的取消和超时控制。

package main

import (
    "context"
    "fmt"
    "time"
)

func worker(ctx context.Context, ch chan<- int) {
    for i := 0; ; i++ {
        select {
        case <-ctx.Done():
            fmt.Println("worker cancelled")
            return
        case ch <- i:
            time.Sleep(time.Millisecond * 100)
        }
    }
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()
    
    ch := make(chan int)
    go worker(ctx, ch)
    
    for {
        select {
        case <-ctx.Done():
            fmt.Println("main cancelled")
            return
        case val := <-ch:
            fmt.Println("Received:", val)
        }
    }
}

Channel最佳实践

使用channel时的一些重要注意事项和最佳实践。

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    // 1. 使用WaitGroup等待goroutine完成
    var wg sync.WaitGroup
    ch := make(chan int)
    
    // 2. 避免goroutine泄漏
    go func() {
        defer wg.Done()
        for val := range ch {
            fmt.Println("Processing:", val)
        }
    }()
    
    // 3. 使用缓冲channel提高性能
    bufferedCh := make(chan int, 10)
    
    // 4. 正确关闭channel
    go func() {
        for i := 0; i < 5; i++ {
            bufferedCh <- i
        }
        close(bufferedCh)
    }()
    
    // 5. 使用select实现非阻塞操作
    select {
    case val := <-bufferedCh:
        fmt.Println("Received:", val)
    default:
        fmt.Println("No data available")
    }
    
    // 6. 使用time.After实现超时
    select {
    case val := <-ch:
        fmt.Println(val)
    case <-time.After(time.Second):
        fmt.Println("Timeout")
    }
}