go并发3
Go语言并发的设计模式和应用场景
以下设计模式和应用场景来自Google IO上的关于Goroutine的PPT:https://talks.golang.org/2012/concurrency.slide
本文的示例代码在: https://github.com/hit9/Go-patterns-with-channel
生成器
在Python中我们可以使用Go中我们可以使用信道来制造生成器(一种lazy load类似的东西)。
当然我们的信道并不是简单的做阻塞主线的功能来使用的哦。
下面是一个制作自增整数生成器的例子,直到主线向信道索要数据,我们才添加数据到信道
func xrange() chan int{ // xrange用来生成自增的整数
var ch chan int = make(chan int)
go func() { // 开出一个goroutine
for i := 0; ; i++ {
ch <- i // 直到信道索要数据,才把i添加进信道
}
}()
return ch
}
func main() {
generator := xrange()
for i:=0; i < 1000; i++ { // 我们生成1000个自增的整数!
fmt.Println(<-generator)
}
}
这不禁叫我想起了Python中可爱的 服务化
比如我们加载一个网站的时候,例如我们登入新浪微博,我们的消息数据应该来自一个独立的服务,这个服务只负责 返回某个用户的新的消息提醒。
如下是一个使用示例:
func get_notification(user string) chan string{
/*
* 此处可以查询数据库获取新消息等等..
*/
notifications := make(chan string)
go func() { // 悬挂一个信道出去
notifications <- fmt.Sprintf("Hi %s, welcome to weibo.com!", user)
}()
return notifications
}
func main() {
jack := get_notification("jack") // 获取jack的消息
joe := get_notification("joe") // 获取joe的消息
// 获取消息的返回
fmt.Println(<-jack)
fmt.Println(<-joe)
}
多路复合
上面的例子都使用一个信道作为返回值,可以把信道的数据合并到一个信道的。 不过这样的话,我们需要按顺序输出我们的返回值(先进先出)。
如下,我们假设要计算很复杂的一个运算 100-x , 分为三路计算, 最后统一在一个信道中取出结果:
func do_stuff(x int) int { // 一个比较耗时的事情,比如计算
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond) //模拟计算
return 100 - x // 假如100-x是一个很费时的计算
}
func branch(x int) chan int{ // 每个分支开出一个goroutine做计算并把计算结果流入各自信道
ch := make(chan int)
go func() {
ch <- do_stuff(x)
}()
return ch
}
func fanIn(chs... chan int) chan int {
ch := make(chan int)
for _, c := range chs {
// 注意此处明确传值
go func(c chan int) {ch <- <- c}(c) // 复合
}
return ch
}
func main() {
result := fanIn(branch(1), branch(2), branch(3))
for i := 0; i < 3; i++ {
fmt.Println(<-result)
}
}
select监听信道
Go有一个语句叫做 如下的程序是select的一个使用例子,我们监视三个信道的数据流出并收集数据到一个信道中。
func foo(i int) chan int {
c := make(chan int)
go func () { c <- i }()
return c
}
func main() {
c1, c2, c3 := foo(1), foo(2), foo(3)
c := make(chan int)
go func() { // 开一个goroutine监视各个信道数据输出并收集数据到信道c
for {
select { // 监视c1, c2, c3的流出,并全部流入信道c
case v1 := <- c1: c <- v1
case v2 := <- c2: c <- v2
case v3 := <- c3: c <- v3
}
}
}()
// 阻塞主线,取出信道c的数据
for i := 0; i < 3; i++ {
fmt.Println(<-c) // 从打印来看我们的数据输出并不是严格的1,2,3顺序
}
}
有了select, 我们在 多路复合中的示例代码中的函数
func fanIn(branches ... chan int) chan int {
c := make(chan int)
go func() {
for i := 0 ; i < len(branches); i++ { //select会尝试着依次取出各个信道的数据
select {
case v1 := <- branches[i]: c <- v1
}
}
}()
return c
}
使用select的时候,有时需要超时处理, 其中的timeout信道相当有趣:
timeout := time.After(1 * time.Second) // timeout 是一个计时信道, 如果达到时间了,就会发一个信号出来
for is_timeout := false; !is_timeout; {
select { // 监视信道c1, c2, c3, timeout信道的数据流出
case v1 := <- c1: fmt.Printf("received %d from c1", v1)
case v2 := <- c2: fmt.Printf("received %d from c2", v2)
case v3 := <- c3: fmt.Printf("received %d from c3", v3)
case <- timeout: is_timeout = true // 超时
}
}
结束标志
在Go并发与并行笔记一我们已经讲过信道的一个很重要也很平常的应用,就是使用无缓冲信道来阻塞主线,等待goroutine结束。
这样我们不必再使用timeout。
那么对上面的timeout来结束主线的方案作个更新:
func main() {
c, quit := make(chan int), make(chan int)
go func() {
c <- 2 // 添加数据
quit <- 1 // 发送完成信号
} ()
for is_quit := false; !is_quit; {
select { // 监视信道c的数据流出
case v := <-c: fmt.Printf("received %d from c", v)
case <-quit: is_quit = true // quit信道有输出,关闭for循环
}
}
}
菊花链

简单地来说,数据从一端流入,从另一端流出,看上去好像一个链表,不知道为什么要取这么个尴尬的名字。。
菊花链的英文名字叫做: Daisy-chain, 它的一个应用就是做过滤器,比如我们来筛下100以内的素数(你需要先知道什么是筛法)
程序有详细的注释,不再说明了。
/*
* 利用信道菊花链筛法求某一个整数范围的素数
* 筛法求素数的基本思想是:把从1开始的、某一范围内的正整数从小到大顺序排列,
* 1不是素数,首先把它筛掉。剩下的数中选择最小的数是素数,然后去掉它的倍数。
* 依次类推,直到筛子为空时结束
*/
package main import "fmt" func xrange() chan int{ // 从2开始自增的整数生成器
var ch chan int = make(chan int) go func() { // 开出一个goroutine
for i := 2; ; i++ {
ch <- i // 直到信道索要数据,才把i添加进信道
}
}() return ch
} func filter(in chan int, number int) chan int {
// 输入一个整数队列,筛出是number倍数的, 不是number的倍数的放入输出队列
// in: 输入队列
out := make(chan int) go func() {
for {
i := <- in // 从输入中取一个 if i % number != 0 {
out <- i // 放入输出信道
}
}
}() return out
} func main() {
const max = 100 // 找出100以内的所有素数
nums := xrange() // 初始化一个整数生成器
number := <-nums // 从生成器中抓一个整数(2), 作为初始化整数 for number <= max { // number作为筛子,当筛子超过max的时候结束筛选
fmt.Println(number) // 打印素数, 筛子即一个素数
nums = filter(nums, number) //筛掉number的倍数
number = <- nums // 更新筛子
}
}
随机数生成器
信道可以做生成器使用,作为一个特殊的例子,它还可以用作随机数生成器。如下是一个随机01生成器:
func rand01() chan int {
ch := make(chan int)
go func () {
for {
select { //select会尝试执行各个case, 如果都可以执行,那么随机选一个执行
case ch <- 0:
case ch <- 1:
}
}
}()
return ch
}
func main() {
generator := rand01() //初始化一个01随机生成器
//测试,打印10个随机01
for i := 0; i < 10; i++ {
fmt.Println(<-generator)
}
}
定时器
我们刚才其实已经接触了信道作为定时器, time包里的 看看我们的定时器吧!
/*
* 利用信道做定时器
*/ package main import (
"fmt"
"time"
) func timer(duration time.Duration) chan bool {
ch := make(chan bool) go func() {
time.Sleep(duration)
ch <- true // 到时间啦!
}() return ch
} func main() {
timeout := timer(time.Second) // 定时1s for {
select {
case <- timeout:
fmt.Println("already 1s!") // 到时间
return //结束程序
}
}
}
TODO
Google的应用场景例子。
本篇主要总结了使用信道, goroutine的一些设计模式。
go并发3的更多相关文章
- .Net多线程编程—并发集合
并发集合 1 为什么使用并发集合? 原因主要有以下几点: System.Collections和System.Collections.Generic名称空间中所提供的经典列表.集合和数组都不是线程安全 ...
- [ 高并发]Java高并发编程系列第二篇--线程同步
高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...
- [高并发]Java高并发编程系列开山篇--线程实现
Java是最早开始有并发的语言之一,再过去传统多任务的模式下,人们发现很难解决一些更为复杂的问题,这个时候我们就有了并发. 引用 多线程比多任务更加有挑战.多线程是在同一个程序内部并行执行,因此会对相 ...
- 关于如何提高Web服务端并发效率的异步编程技术
最近我研究技术的一个重点是java的多线程开发,在我早期学习java的时候,很多书上把java的多线程开发标榜为简单易用,这个简单易用是以C语言作为参照的,不过我也没有使用过C语言开发过多线程,我只知 ...
- 如何在高并发环境下设计出无锁的数据库操作(Java版本)
一个在线2k的游戏,每秒钟并发都吓死人.传统的hibernate直接插库基本上是不可行的.我就一步步推导出一个无锁的数据库操作. 1. 并发中如何无锁. 一个很简单的思路,把并发转化成为单线程.Jav ...
- Java多线程基础——对象及变量并发访问
在开发多线程程序时,如果每个多线程处理的事情都不一样,每个线程都互不相关,这样开发的过程就非常轻松.但是很多时候,多线程程序是需要同时访问同一个对象,或者变量的.这样,一个对象同时被多个线程访问,会出 ...
- 多线程的通信和同步(Java并发编程的艺术--笔记)
1. 线程间的通信机制 线程之间通信机制有两种: 共享内存.消息传递. 2. Java并发 Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式执行,通信的过程对于程序员来说是完全透 ...
- 伪共享(false sharing),并发编程无声的性能杀手
在并发编程过程中,我们大部分的焦点都放在如何控制共享变量的访问控制上(代码层面),但是很少人会关注系统硬件及 JVM 底层相关的影响因素.前段时间学习了一个牛X的高性能异步处理框架 Disruptor ...
- 编写高质量代码:改善Java程序的151个建议(第8章:多线程和并发___建议126~128)
建议126:适时选择不同的线程池来实现 Java的线程池实现从根本上来说只有两个:ThreadPoolExecutor类和ScheduledThreadPoolExecutor类,这两个类还是父子关系 ...
- 理解Storm并发
作者:Jack47 PS:如果喜欢我写的文章,欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 注:本文主要内容翻译自understanding-the-parall ...
随机推荐
- UVALive 7139 Rotation(矩阵前缀和)(2014 Asia Shanghai Regional Contest)
题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&category=6 ...
- 使用sklearn优雅地进行数据挖掘【转】
目录 1 使用sklearn进行数据挖掘 1.1 数据挖掘的步骤 1.2 数据初貌 1.3 关键技术2 并行处理 2.1 整体并行处理 2.2 部分并行处理3 流水线处理4 自动化调参5 持久化6 回 ...
- 使用C#创建快捷方式
在Windows中创建快捷方式很简单,直接用右键点击文件或文件夹,选择创建快捷方式即可.如果想用C#代码的方式创建,就没有那么方便了,因为.NET框架没有提供直接创建快捷方式的方法. 首先我们看一下快 ...
- 在eclipse上开发nodejs
首先到官网下载nodejs.地址:https://nodejs.org/en,可根据自己的操作系统选择下载. 安装好后.进入命令行输入node ,然后输入console.log("hello ...
- Redis的Replication(复制)
文件并发(日志处理)--队列--Redis+Log4Net 分布式中使用Redis实现Session共享(二) http://www.cnblogs.com/stephen-liu74/archive ...
- fetch 关于七牛的content-type 的问题
七牛要的Content-Type: multipart/form-data; boundary=<frontier>:那个boundary(formdata分隔符)是浏览器自己加的,不用管 ...
- 判断一个 int 向量里是否有相同的数(1)
class Solution { public: bool containsDuplicate(vector<int>& nums) { map<int,char> e ...
- Android 子线程测试
private volatile boolean mStopped = false; private int i; TextView tv1; TextView tv2; @Override prot ...
- If you only do what you can do you'll never be more than you are now.
If you only do what you can do you'll never be more than you are now. 如果你只是做你力所能及的事,你将不会有所进步:
- 【python】如何在某.py文件中调用其他.py内的函数
假设名为A.py的文件需要调用B.py文件内的C(x,y)函数 假如在同一目录下,则只需 import B if __name__ == "__main__": B.C(x,y) ...