博客园 首页 新随笔 联系 订阅
|
||||||
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
|---|---|---|---|---|---|---|
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 1 | 2 | 3 | 4 | 5 |
昵称:peiyu1988
园龄:1年
粉丝:20
关注:1 +加关注
搜索
常用链接
我的标签
随笔档案
最新评论
- 1. Re:Nodejs MSSQL详细解读
- @peiyu1988code没有问题,就是按部就班的按照官方文档写了个调用例子,很简单的一个sql语句,select top 1 * from table;但是当表的列数超过120之后就会出现这个错误......
- --coder--面条
- 2. Re:Go语言的核心Routine-Channel
- 收藏收藏
- --mrcorrection
- 3. Re:Nodejs MSSQL详细解读
- @coder--面条调用栈超过阈值, 一般循环调用函数的时候会出现。请确认一下你的code。...
- --peiyu1988
- 4. Re:Nodejs MSSQL详细解读
- 您好,首先看到您的中文详解很钦佩,也很高兴。我在开发中遇到一个关于mssql的问题,可能英文阅读能力太差一直没有找到解决办法,所以在此请教一下,mssql的query执行 select * from ......
- --coder--面条
- 5. Re:Facebook兆级别图片存储及每秒百万级别图片查询原理
- 牛掰
- --webgrossmaker
阅读排行榜
- 1. Nodejs真.多线程处理(5287)
- 2. Nodejs密集型CPU解决方案(1455)
- 3. Facebook兆级别图片存储及每秒百万级别图片查询原理(1248)
- 4. Nodejs MSSQL详细解读(1226)
- 5. Web App适配iPhoneX(1213)
评论排行榜
- 1. Nodejs真.多线程处理(3)
- 2. Nodejs的运行原理-调用篇(3)
- 3. Nodejs的运行原理-架构篇(3)
- 4. Web App适配iPhoneX(3)
- 5. Nodejs MSSQL详细解读(3)
推荐排行榜
- 1. Facebook兆级别图片存储及每秒百万级别图片查询原理(13)
- 2. Nodejs的运行原理-科普篇(10)
- 3. Nodejs的运行原理-函数回调篇(7)
- 4. Nodejs的运行原理-架构篇(6)
- 5. Nodejs的运行原理-模块载入篇(6)
Go语言的核心Routine-Channel
前言
Go语言通过routine,提供了并发编程的支持。
Routine特性
(1) goroutine是Go语言运行库的功能,不是操作系统提供的功能,goroutine不是用线程实现的。
例:启动一个routine
go + 函数名即可启动一个goroutine
?| 1 2 3 4 5 6 7 8 9 10 | package main import ( "fmt" ) func p() { fmt.PrintLn("hello py") } func main() { go p() } |
(2) goroutine就是一段代码,一个函数入口,以及在堆上为其分配的一个堆栈。所以它非常廉价,我们可以很轻松的创建上万个goroutine,但它们并不是被操作系统所调度执行。
(3) 除了被系统调用阻塞的线程外,Go运行库最多会启动$GOMAXPROCS个线程来运行goroutine。
例:使用GOMAXPROCS(设置CPU数)
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | package main import ( "runtime" "time" ) var _ = runtime.GOMAXPROCS(3) var a, b int func u1() { a = 1 b = 2 } func u2() { a = 3 b = 4 } func p() { println(a) println(b) } func main() { go u1() go u2() go p() time.Sleep(1 * time.Second) } |
(4) goroutine是协作式调度的,如果goroutine会执行很长时间,而且不是通过等待读取或写入channel的数据来同步的话,就需要主动调用Gosched()来让出CPU。
例:Gosched()让出CPU
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package main import ( "fmt" "runtime" ) func say(s string) { for i := 0; i < 2; i++ { runtime.Gosched() fmt.Println(s) } } func main() { go say("world") say("hello") } |
(5) 和所有其他并发框架里的协程一样,goroutine里所谓“无锁”的优点只在单线程下有效,如果$GOMAXPROCS > 1并且协程间需要通信,Go运行库会负责加锁保护数据,这也是为什么sieve.go这样的例子在多CPU多线程时反而更慢的原因。
(6) Web等服务端程序要处理的请求从本质上来讲是并行处理的问题,每个请求基本独立,互不依赖,几乎没有数据交互,这不是一个并发编程的模型,而并发编程框架只是解决了其语义表述的复杂性,并不是从根本上提高处理的效率,也许是并发连接和并发编程的英文都是concurrent吧,很容易产生“并发编程框架和coroutine可以高效处理大量并发连接”的误解。
(7) Go语言运行库封装了异步IO,所以可以写出貌似并发数很多的服务端,可即使我们通过调整$GOMAXPROCS来充分利用多核CPU并行处理,其效率也不如我们利用IO事件驱动设计的、按照事务类型划分好合适比例的线程池。在响应时间上,协作式调度是硬伤。
(8) goroutine最大的价值是其实现了并发协程和实际并行执行的线程的映射以及动态扩展,随着其运行库的不断发展和完善,其性能一定会越来越好,尤其是在CPU核数越来越多的未来,终有一天我们会为了代码的简洁和可维护性而放弃那一点点性能的差别。
Routine通信:Channel
routine之间是通过Channel进行通信的;Channel是进程内的通信,不支持进程外的通信。
语法
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | // 声明方式,在此ElemType是指此管道所传递的类型 var chanName chan ElemType // 声明一个传递类型为int的管道 var ch chan int // 声明一个map,元素是bool型的channel var m map[string] chan bool // 定义语法,定义需要使用内置函数make()即可,下面这行代码是声明+定义一个整型管道 ch := make(chan int) // 事先定义好管道的size,下面这行代码定义管道的size为100 ch := make(chan int, 100) // 由管道中读写数据,<-操作符是与最左边的chan优先结合的 // 向管道中写入一个数据,在此需要注意:向管道中写入数据通常会导致程序阻塞,直到有 // 其他goroutine从这个管道中读取数据 ch<- value // 读取数据,注意:如果管道中没有数据,那么从管道中读取数据会导致程序阻塞,直到有数据 value := <-ch // 单向管道 var ch1 chan<- float64 // 只能向里面写入float64的数据,不能读取 var ch2 <-chan int // 只能读取int型数据 // 关闭channel,直接调用close()即可 close(ch) // 判断ch是否关闭,判断ok的值,如果是false,则说明已经关闭(关闭的话读取是不会阻塞的) x, ok := <-ch |
什么样的场景使用管道?
首先我们知道Channel可以在routine中进行通信,如下例:
? package main import "fmt" func print() { fmt.Println("Hello world") } func main() { for i := 0; i < 10; i++ { go print() } } |
上面的代码意思大致是:使用协程来并行输出10次 "Hello world", 但是大家运行上面代码的时候,会发现不会有输出。这是因为虽然使用go关键字进行了协程的创建,但是还没有等到执行的时候,main函数已经退出来了,进程已经关闭,所以起来的协程也不会被执行。
如果你有C相关的多线程经验时,可已经将协程改为线程,之后调用线程的join方法,让主线程等待子线程执行完毕后再退出。而在Go语言中,我们可以利用管道的写入阻塞和读取阻塞来完成类似线程join的行为。代码如下所示:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package main import "fmt" func print(ch chan int) { fmt.Println("Hello world") ch<- 1 } func main() { chs := make([]chan int) for i := 0; i < 10; i++ { chs[i] = make(chan int) go print(chs[i]) } for _, ch := range(chs){ <-ch } } |
通过以上代码,我们就可以完成了并行输出10行Hello world 的效果。
posted on 2018-04-10 19:37 peiyu1988 阅读(92) 评论(1) 编辑 收藏
FeedBack: #1楼 39479962018/4/10 19:40:48 2018-04-10 19:40 mrcorrection 收藏收藏 支持(0) 反对(0) 刷新评论 刷新页面返回顶部 注册用户登录后才能发表评论,请 登录 或 注册,访问网站首页。 【推荐】超50万VC++源码: 大型组态工控、电力仿真CAD与GIS源码库!
【报名】2050 大会 - 博客园程序员团聚(5.25 杭州·云栖小镇)
【招聘】花大价钱找技术大牛我们是认真的!
【腾讯云】买域名送解析+SSL证书+建站
· “支付宝”进入汉语水平考试HSK试题 老外当场疯了
· 联发科第一季度营收17亿美元 同比环比均下滑但符合预期
· Spotify计划推全新版本免费音乐服务 吸引更多新用户
· 扎克伯格解释为何解雇Oculus VR创始人
· 微信和QQ将暂停短视频APP外链直接播放功能
» 更多新闻...
· 写给自学者的入门指南
· 和程序员谈恋爱
· 学会学习
· 优秀技术人的管理陷阱
· 作为一个程序员,数学对你到底有多重要
» 更多知识库文章... 历史上的今天:
2017-04-10 Nodejs真.多线程处理
2017-04-10 Nodejs使用redis
Copyright ©2018 peiyu1988 Powered by: 博客园 模板提供:沪江博客