go非阻塞channel的常见写法

原创文章转载请注明出处

Go 运行时(runtime)管理了一种轻量级线程goroutine,被叫做协程。协程可以使用共享变量来通信,但是很不提倡这样做,因为这种方式给所有的共享内存的多线程都带来了困难。

不要通过共享内存来通信,它们会给你的代码在并发运算的时候带来危险,要通过通信来共享内存。

Go有一个特殊的类型,通道channel像是管道,可以通过它们发送类型化的数据在协程之间通信,可以避开所有内存共享导致的坑;通道的通信方式保证了同步性。数据通过通道:同一时间只有一个协程可以访问数据:所以不会出现数据竞争,设计如此。数据的归属(可以读写数据的能力)被传递。

go非阻塞channel的常见写法_第1张图片
Paste_Image.png

操作符 <- 被叫做 channel 操作符(这个操作符中箭头表明了值的流向)

// 发送 v 到 channel ch
ch <- v
// 接收 channel ch 中的值并赋值给 v
v := <-ch

燃鹅,channel是会阻塞的

func main() {
    var c1 chan string = make(chan string)
    func() {
        time.Sleep(time.Second)
        c1 <- "1"
    }()
    fmt.Println("c1 is", <-c1)
}

上面的例子中,push和pop永远不可能同时发生,会deadlock。

fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main.func1(0xc42001e0c0)
/Users/mac/gowork/src/hello/hello.go:181 +0x63
main.main()
/Users/mac/gowork/src/hello/hello.go:182 +0x5b
exit status 2

如何实现非阻塞channel

  1. 利用go关键字,创建一个新的协程,让push和pop不在同一个协程中执行就可以避免死锁。
func main() {
    var c1 chan string = make(chan string)
    go func() {
        time.Sleep(time.Second)
        c1 <- "1"
    }()
    fmt.Println("c1 is", <-c1)
}
  1. 也可以给channel加一个buffer,当buffer没有被塞满的时候,channel是不会阻塞的。
func main() {
    var c1 chan string = make(chan string, 1)
    func() {
        time.Sleep(time.Second)
        c1 <- "1"
    }()
    fmt.Println("c1 is", <-c1)
}
  1. 利用selectchannel添加默认处理
    select做的就是:选择处理列出的多个通信情况中的一个。
  • 如果都阻塞了,会等待直到其中一个可以处理
  • 如果多个可以处理,随机选择一个
  • 如果没有通道操作可以处理并且写了default语句,它就会执行:default永远是可运行的(这就是准备好了,可以执行)。

select中使用发送操作并且有 default可以确保发送不被阻塞!如果没有caseselect就会一直阻塞。

func main() {
    var c1 chan string = make(chan string)
    var c2 chan string = make(chan string)
    time.Sleep(time.Second)
    select {
    case c := <-c1:
        fmt.Println(c)
    case c := <-c2:
        fmt.Println(c)
    default:
        fmt.Println("After one second!")
    }
}
  1. channel添加超时处理,还是要用到select
func askForC(c chan string) {
    fmt.Println("run in other routine")
}
func main() {
    var c1 chan string = make(chan string)
    var c2 chan string = make(chan string)
    go askForC(c1)
    go askForC(c2)
    select {
    case c := <-c1:
        fmt.Println(c)
    case c := <-c2:
        fmt.Println(c)
    case <-time.After(time.Second):
        fmt.Println("After one second!")
    }
}

我是咕咕鸡,一个还在不停学习的全栈工程师。
热爱生活,喜欢跑步,家庭是我不断向前进步的动力。

你可能感兴趣的:(go非阻塞channel的常见写法)