Golang Go语言中锁实现与Channel实现的区别?
Golang Go语言中锁实现与Channel实现的区别?
最近在读 gRPC 的源码,以下代码截取自 [email protected]/balancer_conn_wrappers.go 文件第 118 行。
// watcher balancer functions sequentially, so the balancer can be implemented
// lock-free.
func (ccb *ccBalancerWrapper) watcher() {
for {
select {
case t := <-ccb.stateChangeQueue.get():
ccb.stateChangeQueue.load()
select {
case <-ccb.done:
ccb.balancer.Close()
return
default:
}
ccb.balancer.HandleSubConnStateChange(t.sc, t.state)
case t := <-ccb.resolverUpdateCh:
select {
case <-ccb.done:
ccb.balancer.Close()
return
default:
}
ccb.balancer.HandleResolvedAddrs(t.addrs, t.err)
case <-ccb.done:
}
select {
case <-ccb.done:
ccb.balancer.Close()
ccb.mu.Lock()
scs := ccb.subConns
ccb.subConns = nil
ccb.mu.Unlock()
for acbw := range scs {
ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
}
return
default:
}
}
}
关于这段代码,我理解是,隐藏 ccb.balancer 的方法调用,转为以 channel 信号触发。其目的是使得 balancer 的方法在实现时不需要考虑并发问题。
我的问题是,这里如果给 balancer 的方法调用都加互斥锁,也能实现同样的目的,那么用 channel 还有什么好处呢?有哪些地方我没有考虑到的,恳请大神们指正。
使用 select 实现同时监听多个 channel 信号,而且使用锁在尝试加锁 Lock 时可能会阻塞,在尝试解锁 Unlock 时可能会触发异常,使用 channel 免去了各种情况都处理,而且可阅读性更强。
channel 的那边实现也是有锁的,channel 还可以实现数据的分发,这些在需要通信的场景下比锁好用多了。
更多关于Golang Go语言中锁实现与Channel实现的区别?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
收敛逻辑在一个协程,这无疑是可读性最高的,最好维护的。
你说的玩锁的方法,一般存在于 c++中,shared_ptr+lock 保证单个资源的同步,但带来的坏处就是并发很复杂,很难看懂。
如果其中一个 channel 在处理中,其他 channel 也会被阻塞,这个和 Lock 类似。
不过也可以实现队列:ccb.stateChangeQueue,来避免阻塞。
Unlock 会触发异常,这个会产生什么异常?使用 channel 可读性更强,目前还没习惯,可能思维还没转换过来。
就为实现“顺序调用对象的 function ”这个问题上,我只是觉得用锁实现的话,代码量会少一点。
上面示例之所以非使用 channel 不可,我觉得是因为 ccb.stateChangeQueue 需要非阻塞,所以要用 Channel,不知道我理解是否正确?
Lock 方法在取到锁前会一直阻塞,如果要监听多把锁需要在不同的协程中进行了,Unlock 一把未锁定的锁会导致 panic,虽然这情况属于逻辑错误,但是使用 channel 就可以减轻思维负担了,channel 本身提供了并发保证,好维护。
锁能提供更细的控制粒度,延迟更低,但也更复杂
在Go语言中,锁(如sync.Mutex
和sync.RWMutex
)与Channel是并发编程中常用的两种同步机制,它们各有优缺点,适用于不同的场景。
锁的实现基于传统的互斥机制,通过加锁和解锁来保护共享资源,防止多个goroutine同时访问导致的数据竞争问题。锁的优势在于可以灵活地控制对共享资源的访问粒度,如读写锁(sync.RWMutex
)可以在读多写少的场景下提高性能。然而,锁的使用也带来了复杂性,如死锁和优先级反转等问题,需要开发者谨慎处理。
Channel则是Go语言特有的一种并发原语,通过goroutine间的通信来实现同步。Channel的优势在于其简洁性和安全性,通过发送和接收操作,可以自然地实现goroutine的协调,避免显式锁的使用。此外,Channel还支持选择性接收和超时机制,提供了更丰富的同步功能。然而,Channel的适用场景相对有限,主要适用于生产者-消费者模型等需要明确通信关系的场景。
综上所述,锁和Channel在Go语言的并发编程中各有优势。在选择使用哪种同步机制时,需要根据具体的应用场景和需求进行权衡。如果需要对共享资源进行细粒度的访问控制,锁可能是一个更好的选择;如果需要在goroutine间进行简洁、安全的通信,Channel则是一个更合适的选择。