golang-chanel 并发设计 以及使用常见问题 1

  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

image.png
image.png

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

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

  • 具体channel里面的各种功能可以参考源码runtime/chan.go:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
}
  1. channel使用中遇到过的问题有哪些?怎么解决?

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

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

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

  • 要考虑兼容此类情况,不能说已经close掉了channel就一定没有数据了,代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
go复制代码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进行读取,导致复杂度提升

  • 通过传参进行限制只读或者是只写,代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
go复制代码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)

}
  1. 参考链接

csp 和 actor的区别:

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%