go语言共享内存通信 go 内存共享( 二 )


queue with a lock prefered to lock-free implementation:
比起完全 lock-free 的实现 , 使用锁的队列实现更简单,容易实现
Channel简易教程 不同于传统的多线程并发模型使用共享内存来实现线程间通信的方式,golang 的哲学是通过 channel 进行协程(goroutine)之间的通信来实现数据共享 。这种方式的优点是通过提供原子的通信原语,避免了竞态情形(race condition)下复杂的锁机制 。
channel 可以看成一个 FIFO 队列,对 FIFO 队列的读写都是原子的操作,不需要加锁 。对 channel 的操作行为结果总结如下:
读取一个已关闭的 channel 时,总是能读取到对应类型的零值,为了和读取非空未关闭 channel 的行为区别 , 可以使用两个接收值:
golang 中大部分类型都是值类型(只有 slice / channel / map 是引用类型),读/写类型是值类型的 channel 时,如果元素 size 比较大时 , 应该使用指针代替,避免频繁的内存拷贝开销。
main方法里创建了一个string类型的Channel,实现主协程与子协程 go SendMessage 进行通信 。主协程执行到 -values 时发生阻塞 , 等待读取 valuesChannel的值,而子协程执行 SendMessage 方法时写入 valuesChannel 。即,主协程发生阻塞 , 等待子协程执行完毕后再继续执行 。
执行的结果如下:
从日志可以看出,打印val语句必须在子协程 go SendMessage 执行完成后才执行 。
如果在此基础上添加多一个协程写入 valuesChannel会发生什么?
在主协程中 , 启动两个子协程给Channel写数据 。而在 SendMessage 方法里,为了达到显示效果,在写入Channel前先睡眠1秒,在主协程也添加睡眠时间 。
执行日志打印如下:
发现只有其中一个协程完成写入Channel的操作 。因为此Channel没有设置缓存,所有只能保存一个写入值 。
那么如何才能保证两个子协程能正常写入Channel呢?
为了保证上面的两个子协程能顺利地把值写入Channel , 我们创建一个带缓冲的Channel 。
新创建的Channel缓冲两个值,这样就能保证两个子协程能正常写入到Channel中 。下面看看打印日志:
如我们猜想一样,两个子协程都能顺利结束 。
晚安~
用生产者消费者理解golang channel生产者消费者问题是一个著名的线程同步问题,该问题描述如下:有一个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个具有多个缓冲区的缓冲池,生产者将它生产的产品放入一个缓冲区中 , 消费者可以从缓冲区中取走产品进行消费,显然生产者和消费者之间必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个已经放入产品的缓冲区中再次投放产品 。
golang 的channel天生具有这种特性,即
①缓冲区满时写 , 缓冲区空时读,都会阻塞 。
②channel 本身就是并发安全的 。
golang实现多生产者多消费者:
运行结果:
可以看出,用golang实现生产者消费者非常简单,PV操作不需要各种加锁解锁,奥妙就在于CSP模型,即golang提倡的用通信代替共享内存 。
Go CSP并发模型Go的CSP并发模型
Go实现了两种并发形式 。第一种是大家普遍认知的:多线程共享内存 。其实就是Java或者C++等语言中的多线程开发 。另外一种是Go语言特有的,也是Go语言推荐的:CSP(communicating sequential processes)并发模型 。
CSP 是 Communicating Sequential Process 的简称,中文可以叫做通信顺序进程 , 是一种并发编程模型,由 Tony Hoare 于 1977 年提出 。简单来说,CSP 模型由并发执行的实体(线程或者进程)所组成,实体之间通过发送消息进行通信,这里发送消息时使用的就是通道 , 或者叫 channel 。CSP 模型的关键是关注 channel,而不关注发送消息的实体 。Go 语言实现了 CSP 部分理论。

推荐阅读