所有go语言的学习者都会看到这样一句话“使用通信来共享内存,而不是通过共享内存来通信”,这是go语言并发编程的座右铭,然而却不那么好理解。
为了搞清楚熟悉的锁模式并发编程和go的channel模式并发编程的区别,先分别看一下这两种模式都是怎么做的:
为了行文简洁,暂时把代码执行单元都称为“线程”,在go语言中都是go routine。线程和go routine的关系涉及go 运行时的实现,已经超出了本文讨论范围。
在锁模式中,一块内存可以被多个线程同时看到,所以叫共享内存。线程之间通过改变内存中的数据来通知其他线程发生了什么,所以是通过共享内存来通信。锁是为了保护一个线程对内存操作的逻辑完整性而引入的一种约定,注意是一种约定而不是规则(一个线程可以不获取锁就操作内存,也可以解锁其他线程加的锁从而破坏保护,这种错误很难发现)。这种约定要每个线程的编写人员自觉遵守,否则就会出现多线程问题,如数据被破坏,死锁,饥饿等。
在go模式中,一块内存同一时间只能被一个线程看到,另外一个线程要操作这块内存,需要当前线程让渡所有权,这个所有权的让渡过程是“通信”。通信的原子性由channel封装好了,内存同一时间只能被同一线程使用,所以这种模式下不需要显示的锁。然而go模式也有约定,如果传递的是内存的指针,或者是控制消息,还是等于共享了内存,还是要保证将所有权让渡后, 不能再操作这块内存,文末举了一个例子。在实际编写代码的时候,按照go模式,一般内存块使用的是局部变量,很难有机会在让渡所有权以后再操作同一块内存。
如果你发现需要持有让渡所有权的