Home > Article > Backend Development > Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations]
What do the various concurrency modes in
golang look like? The following article will demonstrate Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] concurrency for you through 20 animated pictures. I hope it will be helpful to everyone!
#If you prefer to learn about this article through a video, please click to watch my talk at Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations]pherCon www.youtube.com/watch? v=KyuFeiG3Y6...
One of the most powerful features of the Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] language is the built-in concurrency implemented based on Tony Hoare's CSP This paper. Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] was designed with this in mind Concurrency and allows us to build complex concurrency pipelines. Have you ever wondered what the various concurrency modes look like?
You must have thought about it. Most of the time we think about problems through imagination. If I ask you a question about "numbers from 1 to 100", a series of pictures will subconsciously appear in your mind. For example, I would imagine it as a straight line starting from me, going from the numbers 1 to 20 and then turning right 90 degrees all the way to 1000. I remember when I was very young, in our kindergarten, there were a lot of numbers written on the wall in the cloakroom, and the number 20 happened to be around the corner. You probably have your own picture of numbers. Another common example is the visual representation of the seasons of the year - some imagine it as a box, others as a circle.
In any case, I want to use Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] and WebGL to show you my concrete attempts at common concurrency patterns. This more or less represents my understanding of concurrent programs. It would be really interesting to hear how I differ from the picture you have in your head. I'd particularly like to know how Rob Pike or Sameer Ajmani picture concurrency in their heads. I bet I'd be interested. [Related recommendations: Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] Video Tutorial]
So, let’s start our topic today with a very basic “Hello, Concurrent World” example.
The code is very simple - single channel, single goroutine, single write, Single read.
package main func main() { // 创建一个int类型的通道 ch := make(chan int) // 开启一个匿名 goroutine go func() { // 向通道发送数字42 ch <p><a href="https://divan.dev/demos/hello/" target="_blank">Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] to interactive WebGL animation</a> <img src="https://img.php.cn/upload/article/000/000/024/03104ecc960e1992d7b04ec4cd79a77f-0.gif" alt="Hello, World"></p><p>The blue line represents the goroutine running over time. The blue line connecting 'main' and 'go #19' The thin colored lines are used to mark the start and end of the goroutine and show the parent-child relationship. Finally, the red line represents the send/receive action. Although these are two independent actions, I tried to use a "send from A to B" animation to combine them Represented as an action. The "#19" in the goroutine name is the real internal ID of the goroutine. For its acquisition method, refer to Scott Mansfield's "Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations]routine IDs" article. </p><h3> <span class="header-link octicon octicon-link"></span><strong><span style="font-size: 18px;">Timers</span></strong> </h3><p> Actually, you can build a simple timer by creating a channel , start a goroutine to write data to the channel after a specified time interval, and then return the channel to the caller. So the calling function will block when reading the channel until the previously set time interval has passed. Next we call the timer 24 times and try to visualize the calling process. </p><pre class="brush:php;toolbar:false">package main import "time" func timer(d time.Duration) <p><a href="https://divan.dev/demos/timers/" target="_blank">Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] to Interactive WebGL Animation</a> <img src="https://img.php.cn/upload/article/000/000/024/6e2b2d910982ba58209c1c53cd9c7f6d-1.gif" alt="Recurrent Timers"></p><p> Pretty neat, right? Let’s move on. </p><h3> <span class="header-link octicon octicon-link"></span><strong><span style="font-size: 18px;">Ping-pong</span></strong> </h3><p>This concurrency example is taken from Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations]ogle employee Sameer Ajmani "<a href="https://talks.golang.org/2013/advconc.slide#1" target="_blank" textvalue="Advanced Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] Concurrency Patterns">Advanced Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] Concurrency Patterns </a>" speech. Of course, this pattern isn't very advanced, but it may seem new and interesting to those who are only familiar with Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations]'s concurrency mechanics. </p><p>Here we use a channel to represent the table tennis table. An integer variable represents the ball, and then two goroutines are used to represent the player. The player simulates the batting action by increasing the value of the integer variable (click counter). </p><pre class="brush:php;toolbar:false">package main import "time" func main() { var Ball int table := make(chan int) go player(table) go player(table) table <p><a href="https://divan.dev/demos/pingpong/" target="_blank" textvalue="转到交互式 WebGL 动画">Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] to interactive WebGL animation</a> <img src="https://img.php.cn/upload/article/000/000/024/6e2b2d910982ba58209c1c53cd9c7f6d-2.gif" alt="Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations]"></p><p>Here I suggest you click on the <a href="https://divan.dev/demos/pingpong/" target="_blank"> link</a> to enter interactive WebGL animation. You can Slow down or speed up the animation and view it from different angles. </p><p>Now, let’s add three players and take a look. </p><pre class="brush:php;toolbar:false"> go player(table) go player(table) go player(table)
转到交互式 WebGL 动画 我们可以看到每个玩家都按照次序轮流操作,你可能会想为什么会这样。为什么多个玩家(goroutine)会按照严格的顺序接到“球”呢。
答案是 Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] 运行时环境维护了一个 接收者 FIFO 队列 (存储需要从某一通道上接收数据的goroutine),在我们的例子里,每个玩家在刚发出球后就做好了接球准备。我们来看一下更复杂的情况,加入100个玩家。
for i := 0; i <p><a href="https://divan.dev/demos/pingpong100/" target="_blank">转到交互式 WebGL 动画</a> <img src="https://img.php.cn/upload/article/000/000/024/f022e6361217c7f4c90e16d8d0885f99-4.gif" alt="Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] 100"> </p><p>先进先出顺序很明显了,是吧? 我们可以创建一百万个goroutine,因为它们很轻量,但是对于实现我们的目的来说没有必要。我们来想想其他可以玩的。 例如, 常见的消息传递模式。</p><h3> <span class="header-link octicon octicon-link"></span><strong><span style="font-size: 18px;">Fan-In</span></strong> </h3><p>并发世界中流行的模式之一是所谓的 <em>fan-in</em> 模式。这与 <em>fan-out</em> 模式相反,稍后我们将介绍。简而言之,fan-in 是一项功能,可以从多个输入中读取数据并将其全部多路复用到单个通道中。</p><p>举例来说:</p><pre class="brush:php;toolbar:false">package main import ( "fmt" "time" ) func producer(ch chan int, d time.Duration) { var i int for { ch <p><a href="https://divan.dev/demos/fanin/" target="_blank">Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] to interactive WebGL animation</a> <img src="https://img.php.cn/upload/article/000/000/024/f022e6361217c7f4c90e16d8d0885f99-5.gif" alt="Fan-In Pattern"></p><p>如我们所见,第一个 <em>producer</em> 每100毫秒生成一次值,第二个每250毫秒生成一次值,但是 <em>reader</em> 会立即从这两个生产者那里接受值。实际上,多路复用发生在 <em>main</em> 的range循环中。</p><h3> <span class="header-link octicon octicon-link"></span><strong><span style="font-size: 18px;">Workers</span></strong> </h3><p>与 <em>fan-in</em> 相反的模式是 <em>fan-out</em> 或者<em>worker</em> 模式。多个 goroutine 可以从单个通道读取,从而在CPU内核之间分配大量的工作量,因此是 <em>worker</em> 的名称。在Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations]中,此模式易于实现-只需以通道为参数启动多个goroutine,然后将值发送至该通道-Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations]运行时会自动地进行分配和复用 :)</p><pre class="brush:php;toolbar:false">package main import ( "fmt" "sync" "time" ) func worker(tasksCh <p><img src="https://img.php.cn/upload/article/000/000/024/bc82f8929dc05185db5d2b4542d2ca73-6.gif" alt="Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations]"></p><p>这里值得一提的是:并行性。如您所见,所有goroutine并行’运行‘,等待通道给予它们’工作‘。鉴于上面的动画,很容易发现goroutine几乎立即接连地收到它们的工作。不幸的是,该动画在goroutine确实在处理工作还是仅仅是在等待输入的地方没有用颜色显示出来,但是此动画是在GOMAXPROCS=4的情况下录制的,因此只有4个goroutine有效地并行运行。我们将很快讨论这个主题。</p><p>现在,让我们做一些更复杂的事情,并启动一些有自己workers(subworkers)的workers。</p><pre class="brush:php;toolbar:false">package main import ( "fmt" "sync" "time" ) const ( WORKERS = 5 SUBWORKERS = 3 TASKS = 20 SUBTASKS = 10 ) func subworker(subtasks chan int) { for { task, ok := <p><a href="https://divan.dev/demos/workers2/" target="_blank">Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] to interactive WebGL animation</a> <img src="https://img.php.cn/upload/article/000/000/024/bc82f8929dc05185db5d2b4542d2ca73-7.gif" alt="Workers of workers"> 很好。当然,我们可以将worker和subworker的数量设置为更高的值,但是我试图使动画清晰易懂。</p><p>更酷的 fan-out 模式确实存在,例如动态数量的worker/subworker,通过通道发送通道,但是 fan-out 的想法现在应该很清楚了。</p><h3> <span class="header-link octicon octicon-link"></span><strong><span style="font-size: 18px;">服务器</span></strong> </h3><p>下一个常见的模式类似于扇出,但是会在很短的时间内生成goroutine,只是为了完成某些任务。它通常用于实现服务器-创建侦听器,循环运行accept()并为每个接受的连接启动goroutine。它非常具有表现力,可以实现尽可能简单的服务器处理程序。看一个简单的例子:</p><pre class="brush:php;toolbar:false">package main import "net" func handler(c net.Conn) { c.Write([]byte("ok")) c.Close() } func main() { l, err := net.Listen("tcp", ":5000") if err != nil { panic(err) } for { c, err := l.Accept() if err != nil { continue } go handler(c) } }
这不是很有趣-似乎并发方面没有发生任何事情。当然,在引擎盖下有很多复杂性,这是我们特意隐藏的。 “简单性很复杂”.
但是,让我们回到并发性并向我们的服务器添加一些交互。假设每个处理程序都希望异步写入记录器。在我们的示例中,记录器本身是一个单独的goroutine
,它可以完成此任务。
package main import ( "fmt" "net" "time" ) func handler(c net.Conn, ch chan string) { ch <p><a href="https://divan.dev/demos/servers2/" target="_blank">Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] to 交互式WebGL动画</a> <img src="https://img.php.cn/upload/article/000/000/024/dfdd69afd5511ae20942d0af033a1db2-9.gif" alt="Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] 2"> </p><p>不是吗?但是很容易看到,如果请求数量增加并且日志记录操作花费一些时间(例如,准备和编码数据),我们的* logger * goroutine很快就会成为瓶颈。我们可以使用一个已知的扇出模式。我们开始做吧。</p><h3> <span class="header-link octicon octicon-link"></span><strong><span style="font-size: 18px;">服务器+工作者</span></strong> </h3><p>带工作程序的服务器示例是记录器的高级版本。它不仅可以完成一些工作,而且还可以通过* results *通道将其工作结果发送回池中。没什么大不了的,但是它将我们的记录器示例扩展到了更实际的示例。</p><p>让我们看一下代码和动画:</p><pre class="brush:php;toolbar:false">package main import ( "net" "time" ) func handler(c net.Conn, ch chan string) { addr := c.RemoteAddr().String() ch <p><a href="https://divan.dev/demos/servers3/" target="_blank">Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] to 交互式WebGL动画</a> <img src="https://img.php.cn/upload/article/000/000/024/d04fb172e580bf3c4b0bca0edac0a234-10.gif" alt="Server + Worker"> 我们在4个goroutine之间分配了工作,有效地提高了记录器的吞吐量,但是从此动画中,我们可以看到记录器仍然可能是问题的根源。成千上万的连接在分配之前会汇聚在一个通道中,这可能导致记录器再次成为瓶颈。但是,当然,它会在更高的负载下发生。</p><h3> <span class="header-link octicon octicon-link"></span><strong><span style="font-size: 18px;">并发素筛(素筛指素数筛法)</span></strong> </h3><p>足够的扇入/扇出乐趣。让我们看看更复杂的并发算法。我最喜欢的例子之一是Concurrent Prime Sieve,可以在[<a href="https://talks.golang.org/2012/concurrency.slide" target="_blank">Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] Concurrency Patterns</a>]对话中找到。素数筛,或<a href="https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes" target="_blank">[Eratosthenes筛)</a>是一种古老的算法,用于查找达到给定限制的素数。它通过按顺序消除所有质数的倍数来工作。天真的算法并不是真正有效的算法,尤其是在多核计算机上。</p><p>该算法的并发变体使用goroutine过滤数字-每个发现的素数一个goroutine,以及用于将数字从生成器发送到过滤器的通道。找到质数后,它将通过通道发送到* main *以进行输出。当然,该算法也不是很有效,特别是如果您想找到大质数并寻找最低的Big O复杂度,但是我发现它非常优雅。</p><pre class="brush:php;toolbar:false">// 并发的主筛 package main import "fmt" // 将序列2、3、4,...发送到频道“ ch”。 func Generate(ch chan<- int) { for i := 2; ; i++ { ch <- i // Send 'i' to channel 'ch'. } } //将值从通道“ in”复制到通道“ out”, //删除可被“素数”整除的那些。 func Filter(in <-chan int, out chan<- int, prime int) { for { i := <-in // Receive value from 'in'. if i%prime != 0 { out <- i // Send 'i' to 'out'. } } } //主筛:菊花链过滤器过程。 func main() { ch := make(chan int) // Create a new channel. go Generate(ch) // Launch Generate goroutine. for i := 0; i < 10; i++ { prime := <-ch fmt.Println(prime) ch1 := make(chan int) go Filter(ch, ch1, prime) ch = ch1 } }
,请以交互模式随意播放此动画。我喜欢它的说明性-它确实可以帮助您更好地理解该算法。 * generate * goroutine发出从2开始的每个整数,每个新的goroutine仅过滤特定的质数倍数-2、3、5、7 …,将第一个找到的质数发送给* main *。如果旋转它从顶部看,您会看到从goroutine发送到main的所有数字都是质数。漂亮的算法,尤其是在3D中。
现在,让我们回到我们的工作人员示例。还记得我告诉过它以GOMAXPROCS = 4运行吗?那是因为所有这些动画都不是艺术品,它们是真实程序的真实痕迹。
让我们回顾一下GOMAXPROCS是什么。
GOMAXPROCS设置可以同时执行的最大CPU数量。
当然,CPU是指逻辑CPU。我修改了一些示例,以使他们真正地工作(而不仅仅是睡觉)并使用实际的CPU时间。然后,我运行了代码,没有进行任何修改,只是设置了不同的GOMAXPROCS值。 Linux机顶盒有2个CPU,每个CPU具有12个内核,因此有24个内核。
因此,第一次运行演示了该程序在1个内核上运行,而第二次-使用了所有24个内核的功能。
WebGL动画-1| WebGL动画-24GOMAXPROCS1
The speed of time in these animations is different (I want all animations to fit the same time/height), so the difference is noticeable. When GOMAXPROCS = 1, the next worker can only start actual work after the previous one is completed. In the case of GOMAXPROCS = 24, the speedup is very large, while the overhead of multiplexing is negligible.
However, it is important to understand that increasing GOMAXPROCS does not always improve performance and in some cases can actually make it worse.
What can we prove from concurrency times in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations]? One thing that comes to mind is goroutine leaks. For example, if you start a goroutine but it goes out of scope , a leak may occur. Or you simply forgot to add the end condition and ran a for{} loop instead.
The first time I encountered a goroutine leak in my code, scary images appeared in my mind, and over the next weekend I wrote expvarmon. Now I can visualize this scary image using WebGL.
Take a look:
It pains me just to see this :) All these lines are a waste of resources and are part of your program Time bomb.
The last thing I want to make clear is the difference between parallelism and concurrency difference. This topic covers a lot, and Rob Pike gave a wonderful speech on this topic. Truly one of the #must-watch videos.
In short,
Parallelism is simply running things in parallel.
Concurrency is a method of constructing programs.
Thus, concurrent programs may or may not be parallel, and these concepts are somewhat orthogonal. We already saw this when we demonstrated the effect of the GOMAXPROCS setting.
I could repeat all these linked articles and talks, but a picture speaks a thousand words. What I can do here is visualize the difference. So it's parallel. Many things run in parallel.
This is also parallelism:
But this is concurrent:
And this:
This is also concurrent:
gotracer and the gothree.js libraries. First, gotracer does the following:
However, this method is very limited. I had to choose my examples exactly, rename channels and goroutines, to make the more or less complex code produce correct traces. With this approach, there is no easy way to relate channels between goroutines if they have different names. Not to mention the channels sent over chan type channels. There are also big issues with timing - outputting to stdout can take more time than sending the value, so in some cases I have to put a time.Sleep (some milliseconds) in order to get the animation right.
Basically, this is why I don't have open source code yet. I'm playing around with Dmitry Vyukov's Execution Tracker and it seems to provide details of the event, but doesn't include sending worthy information. Maybe there is a better way to achieve the desired goal. If you have ideas, drop me a line on twitter or drop me a line in the comments. It would be great if this two-week tool could be expanded into a real debugging/tracing tool for any Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] program.
I'd also love to see more interesting concurrency algorithms and patterns not listed here. Feel free to write one in the comments.
Happy coding!
UPD: This tool is available at github.com/pan/gotrace and is generated using Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] Execution Tracer and a patched runtime track.
Also, I am open to new jobs, so if your company/team is interested in me, has a hard problem to solve using Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations] in one article [20 animated picture demonstrations], I can work remotely (or you are in Barcelona) and are hiring, please let me know :)
English original address: https://divan.dev/posts/go_concurrency_visualize/
For more programming-related knowledge, please visit: Programming Video ! !
The above is the detailed content of Detailed explanation of concurrency in Go in one article [20 animated picture demonstrations]. For more information, please follow other related articles on the PHP Chinese website!