• golang channel


    一、概念:

    1、channel是可以让一个 goroutine 发送特定值到另一个 goroutine 的通信机制,channel 的发送方和接受方是 goroutine 对象,属于内存级别的通信

    2、channel 在多并发操作里是属于协程安全的,并且遵循了 FIFO 特性。即先执行读取的 goroutine 会先获取到数据,先发送数据的 goroutine 会先输入数据

    特性箴言:

    1.给一个 nil channel 发送数据,造成永远阻塞

    2.从一个 nil channel 接收数据,造成永远阻塞

    3.给一个已经关闭的 channel 发送数据,引起 panic

    4.从一个已经关闭的 channel 接收数据,如果缓冲区中为空,则返回一个零值

    5.无缓冲的channel是同步的,而有缓冲的channel是非同步的

    二、Channel的创建

    ch := make(chan int)
    

    上面是创建了无缓冲的 channel,一旦有 goroutine 往 channel 发送数据,那么当前的 goroutine 会被阻塞住,直到有其他的 goroutine 消费了 channel 里的数据,才能继续运行

    还有另外一种是有缓冲的 channel,它的创建是这样的:

    ch := make(chan int, 2)
    

    第二个参数表示 channel 可缓冲数据的容量。只要当前 channel 里的元素总数不大于这个可缓冲容量,则当前的 goroutine 就不会被阻塞住

    三、channel的读写

    往一个channel发送数据,可以这样写

    ch := make(chan int)
    ch <- 1
    

    对应的读操作

    data <- ch
    

    当我们不再使用 channel 的时候,可以对其进行关闭:

    close(ch)

    当 channel 被关闭后,如果继续往里面写数据,则程序会直接 panic 退出。不过读取关闭后的 channel,不会产生 pannic,还是可以读到数据。

    如果关闭后的 channel 没有数据可读取时,将得到零值,即对应类型的默认值。

    为了能知道当前 channel 是否被关闭,可以使用下面的写法来判断。

     if v, ok := <-ch; !ok {
            fmt.Println("channel 已关闭,读取不到数据")
     }

    四、channel和select

    在写程序时,有时并不单单只会和一个 goroutine 通信,当我们要进行多 goroutine 通信时,则会使用 select 写法来管理多个 channel 的通信数据:

    ch1 := make(chan struct{})
        ch2 := make(chan struct{})
    
        // ch1, ch2 发送数据
        go sendCh1(ch1)
        go sendCh1(ch2)
    
        // channel 数据接受处理
        for {
            select {
            case <-ch1:
                doSomething1()
            case <-ch2:
                doSomething2()
            }
        }
    

    五、channel 的 deadlock

    前面提到过,往 channel 里读写数据时是有可能被阻塞住的,一旦被阻塞,则需要其他的 goroutine 执行对应的读写操作,才能解除阻塞状态。

    然而,阻塞后一直没能发生调度行为,没有可用的 goroutine 可执行,则会一直卡在这个地方,程序就失去执行意义了。此时 Go 就会报 deadlock 错误,如下代码

     func main() {
            ch := make(chan int)
            <-ch
    
            // 执行后将 panic:
            // fatal error: all goroutines are asleep - deadlock!
        }
    

    因此,在使用 channel 时要注意 goroutine 的一发一取,避免 goroutine 永久阻塞!

    六、交替打印ABC

    package main
    
    import (
    	"fmt"
    	"sync"
    )
    
    //控制循环次数
    var count = 5
    
    func main() {
    	wg := sync.WaitGroup{}
    	chanA := make(chan int, 1)
    	chanB := make(chan int, 1)
    	chanC := make(chan int, 1)
    	chanA <- 1
    	wg.Add(3)
    	go printA(&wg, chanA, chanB)
    	go printB(&wg, chanB, chanC)
    	go printC(&wg, chanC, chanA)
    	wg.Wait()
    }
    func printA(wg *sync.WaitGroup, chanA, chanB chan int) {
    	defer wg.Done()
    	for i := 0; i < count; i++ {
    		<-chanA
    		fmt.Println("A")
    		chanB <- 1
    	}
    }
    func printB(wg *sync.WaitGroup, chanB, chanC chan int) {
    	defer wg.Done()
    	for i := 0; i < count; i++ {
    		<-chanB
    		fmt.Println("B")
    		chanC <- 1
    	}
    }
    func printC(wg *sync.WaitGroup, chanC, chanA chan int) {
    	defer wg.Done()
    	for i := 0; i < count; i++ {
    		<-chanC
    		fmt.Println("C")
    		chanA <- 1
    	}
    }
  • 相关阅读:
    Delphi以及三方控件的源代码规模
    InitInheritedComponent的执行过程
    poj 3897 Maze Stretching 二分+A*搜索
    一些窗口API函数,比如SetForegroundWindow,SwitchToThisWindow
    终于懂了:WM_PAINT 与 WM_ERASEBKGND(三种情况:用户操作,UpdateWindow,InvalidateRect产生的效果并不相同),并且用Delphi代码验证 good
    窗口绘制有关的消息整理 WM_PAINT, WM_NCPAINT, WM_ERASEBKGND
    WM_PAINT与WM_ERASEBKGND(用户操作和API这两种情况产生消息的顺序有所不同)
    关于WM_ERASEBKGND和WM_PAINT的深刻理解
    offsetHeight在OnLoad中为0的现象
    TWinControl.WMNCPaint对非客户的绘制
  • 原文地址:https://www.cnblogs.com/wuchangblog/p/16374889.html
Copyright © 2020-2023  润新知