Channel(通道)是Go语言中goroutine之间通信的管道。通过channel,goroutine可以安全地发送和接收数据,实现同步和数据共享。
"不要通过共享内存来通信,而应该通过通信来共享内存"
Channel提供了一种类型安全的通信机制,避免了传统并发编程中的竞态条件和锁的复杂性。
package main
import "fmt"
func main() {
// 创建一个int类型的channel
ch := make(chan int)
// 创建一个string类型的channel
messages := make(chan string)
// 创建一个带缓冲的channel
buffered := make(chan int, 5)
fmt.Println("Channel创建成功")
}
| 类型 | 语法 | 说明 |
|---|---|---|
| 无缓冲channel | make(chan T) | 发送和接收必须同时准备好,否则阻塞 |
| 有缓冲channel | make(chan T, n) | 缓冲区未满时发送不阻塞,缓冲区非空时接收不阻塞 |
| 只读channel | <-chan T | 只能接收数据 |
| 只写channel | chan<- T | 只能发送数据 |
package main
import "fmt"
func main() {
ch := make(chan string)
// 在goroutine中发送数据
go func() {
ch <- "Hello, Channel!" // 发送数据到channel
}()
// 从channel接收数据
msg := <-ch // 接收数据
fmt.Println(msg)
}
package main
import "fmt"
func main() {
ch := make(chan int, 3)
// 发送数据
ch <- 1
ch <- 2
ch <- 3
// 关闭channel
close(ch)
// 从已关闭的channel接收数据
for num := range ch {
fmt.Println(num)
}
// 检查channel是否关闭
val, ok := <-ch
if !ok {
fmt.Println("Channel已关闭")
}
}
无缓冲channel也称为同步channel,发送和接收操作必须同时准备好。
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string)
go func() {
time.Sleep(time.Second)
ch <- "数据已准备好"
}()
fmt.Println("等待数据...")
msg := <-ch // 阻塞,直到接收到数据
fmt.Println(msg)
}
有缓冲channel允许在缓冲区未满时异步发送数据。
package main
import "fmt"
func main() {
// 创建容量为3的缓冲channel
ch := make(chan int, 3)
// 发送数据(不会阻塞)
ch <- 1
ch <- 2
ch <- 3
// 接收数据
fmt.Println(<-ch) // 1
fmt.Println(<-ch) // 2
fmt.Println(<-ch) // 3
}
package main
import "fmt"
func main() {
ch := make(chan int, 5)
ch <- 1
ch <- 2
ch <- 3
// 获取channel长度(当前元素数量)
fmt.Println("长度:", len(ch)) // 3
// 获取channel容量
fmt.Println("容量:", cap(ch)) // 5
}
select语句用于处理多个channel操作,类似于switch语句。
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(time.Second)
ch1 <- "来自ch1"
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "来自ch2"
}()
// select会阻塞,直到某个case可以执行
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
}
}
}
package main
import "fmt"
func main() {
ch := make(chan int)
select {
case val := <-ch:
fmt.Println("接收到:", val)
default:
fmt.Println("没有数据可接收") // 立即执行
}
// 非阻塞发送
select {
case ch <- 1:
fmt.Println("发送成功")
default:
fmt.Println("无法发送") // 立即执行
}
}
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string)
go func() {
time.Sleep(2 * time.Second)
ch <- "数据"
}()
select {
case msg := <-ch:
fmt.Println("接收到:", msg)
case <-time.After(time.Second):
fmt.Println("超时!")
}
}
可以指定channel的方向,限制只能发送或只能接收。
package main
import "fmt"
// 只能发送的channel
func send(ch chan<- int) {
ch <- 42
}
// 只能接收的channel
func receive(ch <-chan int) {
val := <-ch
fmt.Println("接收到:", val)
}
func main() {
ch := make(chan int)
go send(ch)
receive(ch)
}
package main
import (
"fmt"
"time"
)
func producer(ch chan<- int) {
for i := 1; i <= 5; i++ {
fmt.Printf("生产: %d\n", i)
ch <- i
time.Sleep(time.Millisecond * 500)
}
close(ch)
}
func consumer(ch <-chan int) {
for num := range ch {
fmt.Printf("消费: %d\n", num)
time.Sleep(time.Second)
}
}
func main() {
ch := make(chan int, 2)
go producer(ch)
consumer(ch)
}
package main
import "fmt"
func generator(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func square(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}
func main() {
// 构建pipeline
nums := generator(1, 2, 3, 4, 5)
squares := square(nums)
// 消费结果
for result := range squares {
fmt.Println(result)
}
}
package main
import (
"fmt"
"time"
)
type Result struct {
URL string
Data string
}
func crawl(url string, results chan<- Result) {
time.Sleep(time.Second) // 模拟网络请求
results <- Result{
URL: url,
Data: fmt.Sprintf("来自 %s 的数据", url),
}
}
func main() {
urls := []string{
"https://example.com/page1",
"https://example.com/page2",
"https://example.com/page3",
}
results := make(chan Result, len(urls))
for _, url := range urls {
go crawl(url, results)
}
for i := 0; i < len(urls); i++ {
result := <-results
fmt.Printf("URL: %s, Data: %s\n", result.URL, result.Data)
}
}