【转】An introduction to using and visualizing channels in Go
An introduction to using and visualizing channels in Go
原文:https://www.sohamkamani.com/blog/2017/08/24/golang-channels-explained/
-------------------------------------------------------------------------
An introduction to using and visualizing channels in Go ➡️
August 24, 2017
If you’re a beginner getting into Go, its mostly quite easy and straightforward. That is, until you get to channels.
At first, everything about channels seems confusing and unintuitive. The fact that not many other popular programming languages have a similar concept, means that channels is one concept that you have to spend some time learning them, if you’re starting your journey with Go.
At the end of this article, you should have all you need to understand how channels work in Go.
Visualizing Goroutines
To understand channels properly, it is essential to know how to visualize Goroutines first.
Let’s start with a simple Goroutine, that takes a number, multiplies it by two, and prints its value (Run this code):
package main
import (
"fmt"
"time"
)
func main() {
n := 3
// We want to run a goroutine to multiply n by 2
go multiplyByTwo(n)
// We pause the program so that the `multiplyByTwo` goroutine
// can finish and print the output before the code exits
time.Sleep(time.Second)
}
func multiplyByTwo(num int) int {
result := num * 2
fmt.Println(result)
return result
}
We can visualize this program as a set of two blocks: one being the main funciton, and the other being the multiplyByTwo goroutine.
The problems with this implementation (that can also be seen from the diagram), is that these two parts of our code are rather disconnected. As a consequence :
- We cannot access the result of
multiplyByTwo
in themain
function. - We have no way to know when the
multiplyByTwo
goroutine completes. As a result of this, we have to pause themain
function by callingtime.Sleep
, which is a hacky solution at best.
Example #1 - Adding a channel to our goroutine
Let’s now look at some code that introduces how to make and use a channel in Go (Run this code):
package main
import (
"fmt"
)
func main() {
n := 3
// This is where we "make" the channel, which can be used
// to move the `int` datatype
out := make(chan int)
// We still run this function as a goroutine, but this time,
// the channel that we made is also provided
go multiplyByTwo(n, out)
// Once any output is received on this channel, print it to the console and proceed
fmt.Println(<-out)
}
// This function now accepts a channel as its second argument...
func multiplyByTwo(num int, out chan<- int) {
result := num * 2
//... and pipes the result into it
out <- result
}
A channel gives us a way to “connect” the different concurrent parts of our program. In this case, we can represent this connection between our two concurrent blocks of code visually :
Channels can be thought of as “pipes” or “arteries” that connect the different concrrent parts of our code.
Directionality
You can also observe that the connection is directional (that’s why theres an arrow, and not just a line). To explain this, take a look at the type definition of the out
argument of the multiplyByTwo
function :
out chan<- int
- The
chan<-
declaration tells us that you can only put stuff into the channel, but not receive anything from it. - The
int
declaration tells us that the “stuff” you put into the channel can only be of theint
datatype.
Although they look like separate parts, chan<- int
can be thought of as one datatype, that describes a “send-only” channel of integers.
Similarly, an example of a “receive-only” channel declaration would look like:
out <-chan int
You can also declare a channel without giving directionality, which means it can send or recieve data :
out chan int
This is actually seen when we create the out
channel in the main
function :
out := make(chan int)
The reason we had to make a bi-directional channel was because we were using it to send data from the multiplyByTwo
function and receive that same data in the main
function.
Blocking code
Statements that send or receive values from channels are blocking inside their own goroutine.
This means that when we try to print the value received (in the main
function) :
fmt.Println(<-out)
The <-out
statement will block the code until some data is received on the out
channel. It helps to then visualize this by splitting the main
block into two parts : the part that runs until its time to wait for the channel to receive data, and the part that is run after.
The second part of main
can only be run once data is received through the channel, which is why the green arrow connects to the second part.
The dotted arrow added here is to show that it is the main
function that started the multiplyByTwo
goroutine.
Example #2 - Two single directional channels
Example #1 can be implemented another way, by using 2 channels : one for sending data to the goroutine, and another for receiving the result (Run this code).
package main
import (
"fmt"
)
func main() {
n := 3
in := make(chan int)
out := make(chan int)
// We now supply 2 channels to the `multiplyByTwo` function
// One for sending data and one for receiving
go multiplyByTwo(in, out)
// We then send it data through the channel and wait for the result
in <- n
fmt.Println(<-out)
}
func multiplyByTwo(in <-chan int, out chan<- int) {
// This line is just to illustrate that there is code that is
// executed before we have to wait on the `in` channel
fmt.Println("Initializing goroutine...")
// The goroutine does not proceed until data is received on the `in` channel
num := <-in
// The rest is unchanged
result := num * 2
out <- result
}
Now, in addition to main
, multiplyByTwo
is also divided into 2 parts: the part before and after the point where we wait on the in
channel (num := <- in
)
Example #3 - Multiple concurrent goroutines
Now consider the case where we want to run multiplyByTwo
concurrently 3 times (Run this code) :
package main
import (
"fmt"
)
func main() {
out := make(chan int)
in := make(chan int)
// Create 3 `multiplyByTwo` goroutines.
go multiplyByTwo(in, out)
go multiplyByTwo(in, out)
go multiplyByTwo(in, out)
// Up till this point, none of the created goroutines actually do
// anything, since they are all waiting for the `in` channel to
// receive some data
in <- 1
in <- 2
in <- 3
// Now we wait for each result to come in
fmt.Println(<-out)
fmt.Println(<-out)
fmt.Println(<-out)
}
func multiplyByTwo(in <-chan int, out chan<- int) {
fmt.Println("Initializing goroutine...")
num := <-in
result := num * 2
out <- result
}
It is important to note that there is no guarantee as to which goroutine will accept which input, or which goroutine will return an output first. All the main
function “knows”, is that it is sending some data into the in
channel, and expects some data to be received on the out
channel.
This can be slightly harder to visualize, but hang in there!
The multiple concurrent goroutines require a different visualization for channels. Here, we see a channel as a kind of “pool” of data (formally known as a buffer). For the purple channel (in
), the main
function puts in data, and one of the initialized goroutines receive the data. There is no information regarding which goroutine takes which data. This is the same for the green out
channel going back into the main
routine.
The main
routine is now split into 4 parts, since 3 parts now correspond to the 3 times we have to wait ont he out
channel (as described by the 3 fmt.Println(<-out)
statements), and another part for the operations before the first fmt.Println(<-out)
statement.
The multiplyByTwo
goroutines on their own, look (and function) the same as before.
Going forward from here
A lot of the time, programs written in Go are highly confusing because of the concurrency and asynchronous nature of the code. Visualizing your code before you proceed to write it (or for that matter, visualizing someone else’s code before you modify it), can help a great deal and actually save you time in understanding.
All this being said, channels in Go make concurrent programming much easier than it would be without them, and its hard to appreciate the amount of code that we don't
have to write because of them. Hopefully, these visualizations make it even easier.
【转】An introduction to using and visualizing channels in Go的更多相关文章
- 协程和Goroutines示例
一. 协程的定义 Coroutines are computer-program components that generalize subroutines for non-preemptive m ...
- django channle的使用
频道在PyPI上可用 - 要安装它,只需运行: 参照:https://channels.readthedocs.io/en/latest/introduction.html pip install ...
- RabbitMQ消息队列(一): Detailed Introduction 详细介绍
http://blog.csdn.net/anzhsoft/article/details/19563091 RabbitMQ消息队列(一): Detailed Introduction 详细介绍 ...
- Introduction to Financial Management
Recently,i am learning some useful things about financial management by reading <Essentials of Co ...
- Introduction To Monte Carlo Methods
Introduction To Monte Carlo Methods I’m going to keep this tutorial light on math, because the goal ...
- An Introduction to Stock Market Data Analysis with R (Part 1)
Around September of 2016 I wrote two articles on using Python for accessing, visualizing, and evalua ...
- R TUTORIAL: VISUALIZING MULTIVARIATE RELATIONSHIPS IN LARGE DATASETS
In two previous blog posts I discussed some techniques for visualizing relationships involving two o ...
- Netty Tutorial Part 1: Introduction to Netty [z]
Netty Tutorial, Part 1: Introduction to Netty Update: Part 1.5 Has Been Published: Netty Tutorial P ...
- 学习笔记之Introduction to Data Visualization with Python | DataCamp
Introduction to Data Visualization with Python | DataCamp https://www.datacamp.com/courses/introduct ...
随机推荐
- Jenkins - 安装并启动Jenkins
1 - 关于Jenkins 构建流水线(build pipeline)工具Jenkins可以轻松地定义和管理各种各样的操作(构建.测试等),并将这些操作像管道pipe一样自由地进行组合,从而自动.流畅 ...
- memcached概述与基本操作
memcached 什么是memcached memcached之前是danga的一个项目,最早是为LiveJournal服务的,当初设计师为了加速LiveJournal访问速度而开发的,后来被很多大 ...
- jmap使用
今天写的服务在处理大文件是出现Java heap space错误,因此结识了jmap jmap是JDK自带的一个工具,可以做jvm性能调优 可以生成dump文件,查询finalize执行队列.Java ...
- 【C/C++开发】C++编译指令#pragma pack的配对使用
C++编译指令#pragma pack的配对使用 #pragma pack可以用来指定C++数据结构的成员变量的内存对齐数值(可选值为1,2,4,8,16). 本文主要是强调在你的头文件中使用pack ...
- nginx做正向代理https遇到SSL_do_handshake()握手失败
SSL_do_handshake() failed (SSL: error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number) wh ...
- 将本机电脑作为自己的网站服务器--基于XAMPP在本地建立wordPress网站
"我不敢说自己从未担心害怕过. 实际上我希望少一点担心害怕,因为它让我分心,让我的神经系统备受煎熬".----马斯克 周日,搞了大半天,为了熟悉wordPress,先在自己的电脑上 ...
- [转帖]Linux企业运维人员最常用150个命令汇总
Linux企业运维人员最常用150个命令汇总 https://clsn.io/clsn/lx998.html 基本上都用过了. 命令 功能说明 线上查询及帮助命令(2个) man 查看命令帮助,命令的 ...
- Django-01-Web框架简介
1. 什么是web框架 框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的舞台来做表演. 对于所有 ...
- Python中的int函数
python帮组文档 class int(x, base=10) Return an integer object constructed from a number or string x, or ...
- mysql删除字符串的前后的空格
update table set field = replace(replace(replace(field,char(9),''),char(10),''),char(13),'');