转投go系列-channel 堵塞问题

本文详细介绍了Go语言中channel的概念及其在并发中的作用,通过正反例展示了如何正确使用channel进行同步和通信。讨论了无缓冲channel的阻塞特性,并分析了因不当使用导致的死锁和内存泄漏问题。强调了Go语言提倡的通过通信来共享内存的设计哲学。
摘要由CSDN通过智能技术生成

channel是Go语言中的一个核心类型,可以把它看成管道。编程难度肯定是有一些的,哈哈。

“Go 强调不要通过共享内存来通讯,而是通过通讯来共享内存。”
很多人都说上面的话,具体怎么理解呢?

chan是一种引用类型,引用类型 channel可用于多个 goroutine 通讯。其内部实现了同步,确保并发安全。

举个例子:

package main
import "fmt"

func main() {
	ch := make(chan int) 
	go func() {        
		for i := 0; i < 5; i++ {
			ch <- i 
			fmt.Println("input", i)
		}
	}()

	for i := 0; i < 5; i++ {
		num := <-ch
		fmt.Println("output", num)
	}
}

打印结果:

$ go run main.go
input 0
output 0
output 1
input 1
input 2
output 2
output 3
input 3
input 4
output 4

可以看到input output都是成对出现的,因为无缓冲chan,当只有读,没有写时,“读”阻塞。当只有写,没有读,那么 “写”阻塞。

  • 接下来是个反例:
package main
func main(){
    ch:=make(chan int)
    ch <- 1    
    go func (){
        <-ch  
    }()
}

这个就会卡死,因为创建goroutine在chan的写之后,永远不会执行读。

  • 另一个反例:
package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("log:", doReq(3*time.Second))
}

func doReq(timeout time.Duration) (res int) {
	ch := make(chan int)
	go func() {
		res := do()
		ch <- res
	}()
	select {
	case res = <-ch:
		return res
	case <-time.After(timeout):
		return 0
	}
}

func do() int {
	time.Sleep(4 * time.Second)
	return 111
}

我们创建了无缓冲ch,goroutine 执行 do 函数并通过ch 将结果发送回main,这是goroutine阻塞,直到main从 ch 接收到结果为止。同时main将在 select 阻塞,直到goroutine 将结果发送给 ch或超时3秒。如果超时先发生,则main将return 0,这会导致goroutine 写 ch 数据一直堵塞。这样内存就泄露啦!

  • 最后一个反例:
package main

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

var rw sync.RWMutex

func write(a int, out chan<- int) {
	for {
		a++
		rw.Lock()
		out <- a
		fmt.Printf("goroutine 写%d\n", a)
		rw.Unlock()
	}
}

func read(in <-chan int) {
	for {
		time.Sleep(time.Millisecond * 500)
		rw.RLock()
		a := <-in
		fmt.Printf("goroutine 读 %d\n", a)
		rw.RUnlock()
	}
}

func main() {
	ch := make(chan int, 5)
	for i := 0; i < 100; i++ {
		go read(ch)
	}
	for i := 0; i < 100; i++ {
		go write(0, ch)
	}
	for {
		runtime.GC()
	}
}

这种就是锁跟chan相互抢占cpu的反例,因为写的时候会不断的上写锁,读的时候拿不到读锁,也就没办法读chan,连带着写chan也会因为达到limit而阻塞,最后写锁也不会被释放。最后就死锁了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值