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 还有什么好处呢?有哪些地方我没有考虑到的,恳请大神们指正。

6 回复

使用 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.Mutexsync.RWMutex)与Channel是并发编程中常用的两种同步机制,它们各有优缺点,适用于不同的场景。

锁的实现基于传统的互斥机制,通过加锁和解锁来保护共享资源,防止多个goroutine同时访问导致的数据竞争问题。锁的优势在于可以灵活地控制对共享资源的访问粒度,如读写锁(sync.RWMutex)可以在读多写少的场景下提高性能。然而,锁的使用也带来了复杂性,如死锁和优先级反转等问题,需要开发者谨慎处理。

Channel则是Go语言特有的一种并发原语,通过goroutine间的通信来实现同步。Channel的优势在于其简洁性和安全性,通过发送和接收操作,可以自然地实现goroutine的协调,避免显式锁的使用。此外,Channel还支持选择性接收和超时机制,提供了更丰富的同步功能。然而,Channel的适用场景相对有限,主要适用于生产者-消费者模型等需要明确通信关系的场景。

综上所述,锁和Channel在Go语言的并发编程中各有优势。在选择使用哪种同步机制时,需要根据具体的应用场景和需求进行权衡。如果需要对共享资源进行细粒度的访问控制,锁可能是一个更好的选择;如果需要在goroutine间进行简洁、安全的通信,Channel则是一个更合适的选择。

回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!