V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
index90
V2EX  ›  Go 编程语言

锁实现与 Channel 实现的区别?

  •  
  •   index90 · 2019-03-27 15:48:19 +08:00 · 3662 次点击
    这是一个创建于 2102 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    5 条回复    2019-03-27 19:14:55 +08:00
    xrlin
        1
    xrlin  
       2019-03-27 17:34:55 +08:00
    使用 select 实现同时监听多个 channel 信号,而且使用锁在尝试加锁 Lock 时可能会阻塞,在尝试解锁 Unlock 时可能会触发异常,使用 channel 免去了各种情况都处理,而且可阅读性更强。

    channel 的那边实现也是有锁的,channel 还可以实现数据的分发,这些在需要通信的场景下比锁好用多了。
    owenliang
        2
    owenliang  
       2019-03-27 18:02:00 +08:00
    收敛逻辑在一个协程,这无疑是可读性最高的,最好维护的。

    你说的玩锁的方法,一般存在于 c++中,shared_ptr+lock 保证单个资源的同步,但带来的坏处就是并发很复杂,很难看懂。
    index90
        3
    index90  
    OP
       2019-03-27 18:03:09 +08:00
    @xrlin 如果其中一个 channel 在处理中,其他 channel 也会被阻塞,这个和 Lock 类似。

    不过也可以实现队列:ccb.stateChangeQueue,来避免阻塞。

    Unlock 会触发异常,这个会产生什么异常?使用 channel 可读性更强,目前还没习惯,可能思维还没转换过来。

    就为实现“顺序调用对象的 function ”这个问题上,我只是觉得用锁实现的话,代码量会少一点。

    上面示例之所以非使用 channel 不可,我觉得是因为 ccb.stateChangeQueue 需要非阻塞,所以要用 Channel,不知道我理解是否正确?
    xrlin
        4
    xrlin  
       2019-03-27 19:11:48 +08:00 via Android
    @index90 Lock 方法在取到锁前会一直阻塞,如果要监听多把锁需要在不同的协程中进行了,Unlock 一把未锁定的锁会导致 panic,虽然这情况属于逻辑错误,但是使用 channel 就可以减轻思维负担了,channel 本身提供了并发保证,好维护。
    hilbertz
        5
    hilbertz  
       2019-03-27 19:14:55 +08:00
    锁能提供更细的控制粒度,延迟更低,但也更复杂
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   940 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 22:42 · PVG 06:42 · LAX 14:42 · JFK 17:42
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.