Go channel使用

本文详细介绍了Golang中的Channel,包括声明与创建、数据传递、缓存与阻塞、空channel、Close操作、接收判断、for-range读取以及Select的使用。通过实例展示了Channel在并发中的数据通信和同步机制,阐述了其工作原理和注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Go channel

Channel 是golang中的管道类型,并且拥有FIFO的特性,经常通过它来实现并发中的数据通信。

声明与创建

// Chan 声明需要指定类型,此chan只能传递int类型数据
var ch chan int  
// 创建  
ch = make(chan int)  
// Chan 创建, 和其他类型一样,也可以使用':='在创建时声明
ch := make(chan int)
ch2 := make(chan interface{})

数据传递

Chan的数据传递需要使用’<-’, ‘<-chan’ 从此chan接收数据, ‘chan<-’ 发送数据到此chan。

package main

import "fmt"

func main() {

    messages := make(chan string)

    go func() { messages <- "ping" }()

    msg := <-messages
    fmt.Println(msg)
}

缓存与阻塞

// 可以在创建时指定chan的缓存大小,默认没有缓存
ch := make(chan int, 100)

如果没有缓存,那么接收方会一直阻塞,直到有数据发送。

package main

import "time"

func main() {
	ch := make(chan int)
	go func() {
		time.Sleep(time.Second)
		ch <- 1
	}()
	<-ch // 这里阻塞一秒
}

如果没有缓存,那么发送方会一直阻塞,直到发送的数据被接收。

package main

import "time"

func main() {
	ch := make(chan int)
	go func() {
		time.Sleep(time.Second)
		<-ch
	}()
	ch <- 1 // 这里阻塞一秒
}

如果使用缓存,那么在缓存没有满时,发送方不会阻塞,缓存已满,发送方会阻塞

package main

import "time"

func main() {
	ch := make(chan int, 5)
	go func() {
		for i := 0; i < 6; i += 1 {
			time.Sleep(time.Second)
			<-ch
		}
	}()
	// 前五次不会阻塞,只有最有一次阻塞,阻塞一秒
	for i := 0; i < 6; i += 1 {
		ch <- i
	}
}

如果使用缓存,那么在缓存不为空时,接收方不会阻塞,缓存为空,接收方会阻塞

package main

import "time"

func main() {
	ch := make(chan int, 5)
	go func() {
		for i := 0; i < 6; i += 1 {
			time.Sleep(time.Second)
			ch <- i
		}
	}()
	// 每次接收消息都在阻塞,阻塞六次
	for i := 0; i < 6; i += 1 {
		<-ch
	}
}

空channel

向空chan发送数据,和接收数据,会一直阻塞

Close

close()函数可以关闭一个chan,向已关闭的chan发送数据进程会panic,从已关闭的chan接收数据会立即返回,返回未接收的消息,如果没有未接收的消息,返回类型的0值。

package main

import (
	"fmt"
)

func main() {
	ch := make(chan int, 1)
	ch <- 1
	close(ch)
	fmt.Println(<-ch)
	fmt.Println(<-ch)
	ch <- 2
}
/*
    1
    0
    panic: send on closed channel

    goroutine 1 [running]:
    main.main()
            /home/zhangsicong/golang/shenjing/shenjing_be/server/test.go:13 +0x19f
    exit status 2
*/

从chan接收数据时,可以接收额外的值来判断chan有没有被关闭
例如:n, ok := <- ch, 如果关闭,ok为false

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int)
    go func() {
        for i := 0; i < 3; i += 1 {
            ch <- i
        }
        close(ch)
    }()
    time.Sleep(time.Second)
    for i := 0; i < 5; i += 1 {
        n, ok := <-ch
        fmt.Println(n, ok)
    }
}
/*
    1 true
    2 true
    3 true
    0 false
    0 false
*/

for range可以读取channel

package main

import (
	"fmt"
)

func main() {
	ch := make(chan int)
	go func() {
		for i := 0; i < 10; i += 1 {
			ch <- i
		}
		close(ch) // 如果没有close, for range将会一直阻塞下去
	}()
	for n := range ch {
		fmt.Println(n)
	}
}

Select

Select会选择任一可操作的chan,如果没有chan可操作,如果存在default,将执行其中的代码,否则将阻塞。

package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int)
	done := make(chan bool)

	go func() {
		for i := 0; i < 10; i += 1 {
			ch <- i
		}
		time.Sleep(2 * time.Second)
		done <- true
	}()

	for {
		select {
		case n := <-ch:
			fmt.Println("Recv", n)
		case <-done:
			fmt.Println("Done")
			return
		default:
			fmt.Println("Wait")
		}
		time.Sleep(100 * time.Millisecond)
	}
}
/*
Wait
Recv 0
Recv 1
Recv 2
Recv 3
Recv 4
Recv 5
Recv 6
Recv 7
Recv 8
Recv 9
Wait
Wait
Wait
Wait
...
Done
*/

定时与channel

package main

import (
	"fmt"
	"time"
)

func main() {
    // 过期返回一次, timeAfter是 chan time.Time
	timeAfter := time.After(100 * time.Millisecond)     
    // 过期返回一次, timer.C是 chan time.Time
	timer := time.NewTimer(100 * time.Millisecond)      
    // 定期返回
	ticker := time.NewTicker(100 * time.Millisecond)   

	for i := 0; i < 10; i += 1 {
		select {
		case <-timeAfter:
			fmt.Println("Time after1")
		case <-timer.C:
			fmt.Println("Timer")
		case <-ticker.C:
			fmt.Println("Ticker")
		}
	}
}
/*
Time after1
Timer
Ticker
Ticker
Ticker
Ticker
Ticker
Ticker
Ticker
Ticker
*/

参考

https://colobu.com/2016/04/14/Golang-Channels/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值