当前位置: 首页 > 科技观察

视觉学习Go并发编程

时间:2023-03-21 10:09:31 科技观察

Parallel&Concurrency如果想了解并行和并发的区别,推荐阅读RobPike的分享(点击阅读原文跳转)。在这个视频中,他用生动的Gopher动画讲解了Go并发的过程。总结一下他的观点如下:并发是非常强大的。并发有助于实现并行性,并使并行性(扩展等)变得更容易。并发不是并行。并发的重点是架构,并行的重点是执行。两者不同,但又相关。因为今天的重点不是理论,而是从视觉的角度展示并发过程。因此,结合这两个直观图形,可以直观地理解并发和并行的区别。毕竟,一张图片胜过一千个字。(请复制链接在浏览器打开)并发:http://talks.bingohuang.com/2017/go-concurrency-visualize/pingpong36.html并发:http://talks.bingohuang.com/2017/go-concurrency-visualize/parallelism.html一般来说,并行是同时执行(通常是相关的)计算任务的编程技术,两个或多个事件同时发生;而并发是将独立的执行过程综合在一起的一种编程技术,其中两个或多个事件在同一时间间隔内发生。为什么要关注并发?今天是多核的时代,并发的世界。摩尔定律正在逐渐失效,需要更多关注并发编程思想。但是并发编程并不容易,Go对并发的支持非常好。Go语言中的ConcurrentGoroutine——并发执行类似于UNIX中的&,很像线程,但是更轻。goroutine是一个独立运行的函数。当一个goroutine阻塞时,它所在的线程会阻塞,但其他goroutine不会受到影响关键字go用于创建goroutine,如下,Channel类似于UNIX中的管道,允许消息在其间传递协程。这里举一个简单的例子,就是一个普通的定时器,后面可以做成可视化的图形。Select类似于语言中常见的switch,但它的判断条件是基于通信的,而不是基于值的等价匹配。Go使并发编程变得更容易,但问题来了:我们如何解释Go的并发性?我们如何看待Go到底如何才能更好的实践Go并发编程呢?这里有一个法宝——GoTrace,是一个开源的gotrace(go),可以可视化Go的并发过程:分析go工具trace的执行结果gothree(js):Generate3DimagebasedonThreeJsandWebGL感谢大神divan提供这个工具和大量的Go并发模式资料。耳闻为实,眼见为实1.你好,世界!不管你写什么语言,都会从helloworld开始。代码非常简单——单通道,单goroutine,一次写入,一次读取。效果如下图(复制链接http://talks.bingohuang.com/2017/go-concurrency-visualize/helloworld.html用浏览器打开或直接访问):蓝线这里表示goroutine超时运行。连接“main”和“#20”的蓝色细线标记了围棋例程的开始和结束,揭示了它们的父子关系。最后,红色箭头向我们展示了“发送/接收”操作。它实际上是我试图作为单个事务设置动画的两个独立操作:从A发送到B。goroutine名称中的“#20”是实际的goroutine内部ID,通过某种方式从运行时获取。2.Timer记得前面Channel给的一个定时器的例子,也很典型——创建一个channel,启动一个goroutine,在给定的时间间隔后向channel写入数据,然后将channel返回给函数调用。调用者将在阅读频道之前阻塞一段固定的时间。让我们运行24个这样的计时器并尝试将它们可视化。看看效果(复制链接http://talks.bingohuang.com/2017/go-concurrency-visualize/timer.html):很形象,是不是?3.两个乒乓球手看效果(复制链接http://talks.bingohuang.com/2017/go-concurrency-visualize/pingpong2.html在浏览器中打开)建议打开上面的链接PC浏览器,您可以与WebGL动画交互并播放它。你可以放慢它的速度,加快它的速度,并从不同的角度看它。三人以上是两个乒乓球运动员对打的常见过程。如果有三个玩家会怎样?现在,让我们尝试让三名玩家一起运行游戏。只需要在代码上做少量修改,添加播放器即可:效果如下:(复制链接http://talks.bingohuang.com/2017/go-concurrency-visualize/pingpong3.html和在浏览器中打开)36名运动员让我们看一个更复杂的例子,运行36名运动员。效果(复制链接http://talks.bingohuang.com/2017/go-concurrency-visualize/pingpong36.html在浏览器中打开)这里我们看到每个玩家轮流出场,大家可能会奇怪为什么是这么?为什么接收goroutines的顺序如此严格?答案是因为Go运行时为接收者(准备好从特定通道接收消息的goroutines)维护了一个先进先出(FIFO)队列,而在我们的例子中,每个玩家都在他准备好的时候准备好击球在桌子上。4.素数筛上面的例子比较简单,让我们看一个更复杂的并发算法:素数筛算法,也称为埃拉托色尼算法,是一种古老的算法,用来寻找小于或等于给定的给定整数n的素数。算法核心思想:先用最小素数2筛选,去掉2的倍数;下一个未筛选的数字是素数(这里是3)。然后用这个素数3去筛,筛出3的倍数……如此反复,直到筛完。该算法的一个并发变体使用goroutines来过滤数字——一个goroutine找到一个素数,然后使用通道将数字从生成器传递到过滤器。当找到质数后,会通过channel传给main,然后输出。当然,这个算法效率不会很高,特别是如果你想找到大量的素数,而且你正在寻找最低的BigO复杂度,但是这个算法非常优雅。大家可以看一下可视化(http://talks.bingohuang.com/2017/go-concurrency-visualize/primesieve.html),体验交互模式下的动画。它的图形方式确实可以帮助我们更好地理解算法。生成函数goroutine发出从2开始的每个整数。每个新的过滤器函数goroutine将过滤特定素数的倍数-2,3,5,7...每个过滤器的第一个数字是素数,将其发送到主要功能和输出。如果旋转图像并从上向下看,从goroutines发送到main的所有数字都是质数。非常好的算法,尤其是对于3D图像。5.Other-Goroutinesleak虽然Goroutine是一个非常轻量级的线程,但是不应该被浪费掉。如果有超过N个Goroutines泄漏怎么办?效果见http://talks.bingohuang.com/2017/go-concurrency-visualize/leak.html。看起来很美,但是是一颗定时炸弹,所以平时写代码一定要注意Goroutine泄露的问题。Gotrace使用介绍下载方法:goget-v-ugithub.com/divan/gotrace默认分支(master)是基于Go1.6开发的。我建议切换到Go18分支以支持Go1.8。具体用法如下:直接运行go代码效果不好,建议生成trace,需要在执行代码前后加上:结合docker,可以使用如下脚本: