goroutine
:是一个通过go
关键字起起来的独立的执行某个 function 的过程,它拥有独立的可以自行管理的调用栈。channels
: 用于goroutine
之间的通讯、同步
一个简单的事务处理的例子
对于下面这样的非并发的程序:
channels 的特性
- goroutine-safe,多个
goroutine
可以同时访问一个 channel
而不会出现竞争问题
- 可以用于在 goroutine 之间存储和传递值
- 其语义是先进先出(FIFO)
- 可以导致 goroutine 的 block 和 unblock
构造 channel
// 带缓冲的 channel
ch := make(chan Task, 3)
// 无缓冲的 channel
ch := make(chan Tass)
如果忽略内置的 channel,让我们自己设计一个具有 goroutines-safe
并且可以用来存储、传递值的东西,该如何做?或许可以用一个带锁的队列来实现。channel 内部就是一个带锁的队列
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[// 发送 index]
recvx uint // receive index[// 接收 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 // 互斥量
}
buf
具体实现就是一个环形队列的实现,sendx
和 recvx
分别用来纪录发送、接收的位置然后用一个 lock
互斥锁来确保无竞争冒险。
对于每一个 ch:=make(chan int,3)
这类操作都会在堆中分配一个空间,简历并且初始化一个 hchan strcut
而 ch
则是指向这个 hchan struct
的指针
因为 ch
本身是个指针,所以我们才可以在 gotoutine
函数调用的时候将 ch
传递过去,而不用再 &ch
取指针了,所以所有使用同一个 ch
的 goroutine
都指向了同一个实际的内存空间
发送、接收
我们用 G1
描述 main()
函数的 goroutine,G2
表示 worker 的 goroutine
// G1
func main() {
...
for _, task := range tasks {
ch <- task
}
...
}
// G2
func worker(task chan int) {
for {
process(task)
}
}
简单的发送接收
G1
中的 ch<-task[0]
具体是怎么是怎么做的呢?
1、获取锁
2、enqueue(task[0]) (这里是内存赋值 task[0])
3、释放锁
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于