1. channel 的设计模式CSP是什么?

1.1 CSP是什么?对于传统的共享内存有什么好处?

  • CSP:Communicating Sequential Processes,顺序通信进程,核心就是多个线程之间通过channel来进行通信。这个也对应golang社区的一句经典语 不要通过共享内存的方式进行通信,而是应该通过通信的方式共享内存
  • 这种设计模式带来的好处有哪些(对比共享内存)?
    • 解耦合,通过数据冗余来避免程序员频繁直接操作 锁,降低编码复杂度
    • 便于分布式通信(目前在golang channel没有体现)

1.2 对比 并发模型 Actor 和 CSP

  • 什么是Actor?
    • Actor模式是一种并发模型,与另一种模型共享内存完全相反,Actor模型share nothing。所有的线程(或进程)通过消息传递(邮箱)的方式进行合作,这些线程(或进程)称为Actor
  • actor 和 CSP 对比图 ?第一张图为actor 第二张为 channel



  • csp模型和actor模型有什么区别
    • CSP 通信中goroutine是和channel 解耦合的,actor 和 传递消息的 “邮箱” 是耦合的
    • CSP 通信是同步的,Actor则是异步的。golang中channel是进行优化后支持缓存
    • CSP 通信中通过channel通信的两个goroutine,由于有channel的存在,所以相互是匿名的,而Actor中的Actor实例则不是
    • CSP 模型能够保证顺序性,而Actor模型不行。比如 当生产为 一个 ,而消费者为多个的时候

2. channel 底层结构是什么样的 ?

  • 具体channel里面的各种功能可以参考源码runtime/chan.go:
type hchan struct {
    qcount   uint           // total data in the queue
    dataqsiz uint           // size of the circular queue
    buf      unsafe.Pointer // points to an array of dataqsiz elements
    elemsize uint16
    closed   uint32
    elemtype *_type // element type
    sendx    uint   // send index
    recvx    uint   // receive index
    recvq    waitq  // list of recv waiters
    sendq    waitq  // list of send waiters

    // lock protects all fields in hchan, as well as several
    // fields in sudogs blocked on this channel.
    //
    // Do not change another G's status while holding this lock
    // (in particular, do not ready a G), as this can deadlock
    // with stack shrinking.
    lock mutex
}

3. channel使用中遇到过的问题有哪些?怎么解决?

3.1 向一个关闭了的channel写入了数据, 导致Panic

  • 解决办法1: 写入的goroutineshi来关闭channel,而不是由读取的channel来进行关闭
  • 解决办法2: 当遇到多个写入的groutine的时候,通过sync.waitGroup来进行阻塞关闭

3.2 一个有缓存的channel已经关闭,还是能够读取到数据

  • 要考虑兼容此类情况,不能说已经close掉了channel就一定没有数据了,代码如下
package main

import (
    "fmt"
    "testing"
    "time"
)


func OnlyRead(testCh <-chan string) {
    result, ok := <-testCh
    fmt.Println(result, ok) // 这里会输出:msg1 true
}

func TestChannel(t *testing.T) {

    testCh := make(chan string, 5)
    testCh <- "msg1"
    close(testCh)
    go OnlyRead(testCh)
    time.Sleep(time.Second)

}

3.3 某个函数中对channel进行写入又对channel进行读取,导致复杂度提升

  • 通过传参进行限制只读或者是只写,代码如下
package main

import (
    "fmt"
    "testing"
    "time"
)

func OnlyWrite(testCh chan<- string) {

    testCh <- "msg2"
}

func OnlyRead(testCh <-chan string) {
    for result := range testCh {
        fmt.Println(result, "====")
    }
}

func TestChannel(t *testing.T) {

    testCh := make(chan string, 2)
    testCh <- "msg1"
    go OnlyRead(testCh)
    go OnlyWrite(testCh)
    time.Sleep(time.Second * 1)
    close(testCh)

}

4. 参考链接

csp 和 actor的区别: