如何理解channel
不知道golang初学者会不会有这样的疑惑,”为什么大家都说golang的channel 和 goroutine是并发编程的大杀器啊?“我一开始也是一头雾水,不理解channel能怎样用,但是看身边的同学把他玩出花,心里一个痒痒的。
通过channel,数据可以在goroutine(协程)中传递信息。我们在协程中可能会有数据交互,举个简单的例子,我们在主线程的生命周期中开一个处理协程,单协程处理dataCh。
之后可以开启其他协程在dataCh中发送数据。这样就初步完成数据交互。
此外,这个这也有一个思想,就是一种生产者——消费者模式。这样的数据传输可以避免不同协程共享内存的问题。数据的消费是有序的。
dataCh := make(chan int, 10)
go func(){
for() {
data := <- dataCh
// handle data
}
}()channel的一些应用
上面的例子告诉我们,管道可以方便数据处理,让数据的处理不凌乱。通过管道,可以把数据放在一个协程上处理。
不过光是看上面的例子,本人还是没办法完全理解,为什么channel就是好呢,所以我也查询了些一些channel的例子。
定时器
定时器可以通过time包自带的方法使用。通过time包启动一个ticker,每过一个duration,就会运行一次
// 定时器
func tickerWorker() {
ticker := time.Tick(time.Second)
for {
select {
case <-ticker:
fmt.Println("ticker")
}
}
}生产者消费者
数据的生产和消费不在同一个协程,解耦两端
// 生产者消费者
func producerConsumer() {
dataCh := make(chan int, 10)
// sender
for i := 0; i < 5; i++ {
go func() {
for {
select {
case dataCh <- rand.Int():
}
}
}()
}
// receiver
for d := range dataCh {
fmt.Println(d)
}
}限流器
我觉得限流器很好玩,定义一个带缓存的管道,每次去管道加个锁,结束后释放
// 限流器,控制速率,当调用下游服务时,可以用channel控制速率
func limit() {
limitCh := make(chan struct{}, 3)
for i := 0; i < 20; i++ {
limitCh <- struct{}{}
go func() {
defer func() { <-limitCh }()
// 这里运行下游逻辑
fmt.Println("这里运行下游逻辑")
}()
}
}
// 经典的channel题目
/*
有 4 个 goroutine,编号为 1、2、3、4。
每秒钟会有一个 goroutine 打印出自己的编号,要求写一个程序,让输出的编号总是按照 1、2、3、4、1、2、3、4… 的顺序打印出来
*/
func run() {
chans := make([]chan int, 4)
for i, _ := range chans {
chans[i] = make(chan int)
}
for i, _ := range chans {
go func(i int) {
ch := chans[i]
for {
select {
case data := <-ch:
fmt.Println(data + 1)
time.Sleep(time.Second)
chans[(i+1)%4] <- (data + 1) % 4
}
}
}(i)
}
chans[0] <- 0
}提示:为什么有了管道,还需要sync.WaitGroup控制goroutine呢,因为如果光靠管道的通信结束协程的话,可能协程还没退出,main就先退出了,为了保障协程先退出
var wg sync.WaitGroup
func work() {
defer wg.Done()
xxxxx
}
func main() {
for xxxx {
wg.Add(1)
work()
}
wg.Wait()
}golang高级编程——常见并发模式
使用context控制并发的超时
func worker(ctx context.Context, wg *sync.WaitGroup) error {
defer wg.Done()
for {
select {
default:
fmt.Println("hello")
case <-ctx.Done():
return ctx.Err()
}
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go worker(ctx, &wg)
}
time.Sleep(time.Second)
cancel()
wg.Wait()
}