翻译进度
11
分块数量
1
参与人数

[奔跑的 Go] 教程十五、深入学习 Go 并发编程之 goroutine

这是一篇协同翻译的文章,你可以点击『我来翻译』按钮来参与翻译。

Golang

goroutine is lightweight execution thread running in background. goroutine is a key ingredient to achieve concurrency in go.

In previous lesson, we learned about go’s concurrency model. As goroutines are lightweight compared to OS threads, it is very common for a go application to have thousands of goroutines running concurrently. Concurrency can speed up application significantly as well as help us write code with separation of concerns (SoC).

有 1 个译文正在审阅中...

☛ What is a goroutine?

We understood in theory that how goroutine works, but in code, what is it? Well, a goroutine is simply a function or method that is running in background concurrently with other goroutines. It’s not a function or method definition that determine if it is a goroutine, it is determined by how we call it.

go provides a special keyword go to create a goroutine. When we call a function or a method with go prefix, that function or method executes in a goroutine. Let’s see a simple example.

https://play.golang.org/p/pIGsToIA2hL

In above program, we created a function printHello which prints Hello World! to the console. In main function, we called printHello() like normal function call and we got desired result.

有 1 个译文正在审阅中...

Now let’s create goroutine from the same printHello function.

https://play.golang.org/p/LWXAgDpTcJP

Well, as per goroutine syntax, we prefixed function call with go keyword and program executed well. It yielded following result

main execution started
main execution stopped

It is a bit strange that Hello World did not get printed. So what happened?

goroutines always runs in the background. When a goroutine is executed, here go printHello(), go does not block the program execution unlike normal function call as we seen in previous example. Instead, the control is returned immediately to the next line of code and any returned value from goroutine is ignored. But even then, why we can’t see the function output?

By default, every go standalone application creates one goroutine. This is known as the main goroutine that the **main**function operates on. In above case, main goroutine spawns another goroutine of printHello function, let’s call it printHello goroutine. Hence when we execute above program, there are two goroutines running concurrently. As we saw in earlier program, goroutines are schedules cooperatively. Hence when main goroutine starts executing, go scheduler dot not pass control to the printHello goroutine until main goroutine does not executes completely. Unfortunately, when main goroutine is done with execution, program terminates immediately and scheduler does not get time to schedule printHello goroutine. But as we know from other lesson, using blocking condition, we can pass control to other goroutines manually AKA telling scheduler to schedule other available gorutines. Let’s use time.Sleep() call to do it.

https://play.golang.org/p/ujQKjpALlRJ

We have modified program in such a way that before main goroutine pass control to the last line of code, we pass control to printHello goroutine using time.Sleep(10 * time.Millisecond) call. In this case, main goroutine sleeps for 10 milli-seconds and won’t be scheduled again for another 10 milli-seconds. Once printHello goroutine executes, it prints ‘Hello World!’ to the console and terminates, then main goroutine is schedules again (after 10 milli-seconds) to execute last line of code where stack pointer is. Hence above program yields following result

main execution started
Hello World!
main execution stopped

If we add a sleep call inside the function which will tell goroutine to schedule another available goroutine, in this case main goroutine. But from last lesson, we learned that only non-sleeping goroutines are considered for scheduling, main won’t be scheduled again for 10 milli-seconds while it’s sleeping. Hence main goroutine will print ‘main execution started’, spawning printHellogoroutine but still actively running, then sleeping for 10 milli-seconds and passing control to printHello goroutine. printHello goroutine the will sleep for 1 milli-second telling scheduler to schedule another goroutine but since there isn’t any available, waking up after 1 milli-second and printing ‘Hello World!’ and then dying. Then main goroutine will wake up after few milli-seconds, printing ‘main execution stopped’ and exiting the program.

https://play.golang.org/p/rWvzS8UeqD6

Above program will still print same result

main execution started
Hello World!
main execution stopped

What if, instead of 1 milli-second, printHello goroutine sleeps for 15 milli-seconds.

https://play.golang.org/p/Pc2nP2BtRiP

In that case, main goroutine will be available to schedule for scheduler before printHello goroutine wakes up, which will also terminate the program immediately before scheduler had time to schedule printHello goroutine again. Hence it will yield below program

main execution started
main execution stopped

☛ Working with multiple goroutines

As I said earlier, you can create as many gorutines as you can. Let’s define two simple functions, one prints characters of the string and one prints digit of the integer slice.

https://play.golang.org/p/SJano_g1wTV

In above program, we are creating 2 goroutines from 2 function calls in series. Then we are scheduling any of the two goroutines and which goroutines to schedule is determined by the scheduler. This will yield following result

main execution started
H e l l o 1 2 3 4 5 
main execution stopped

Above result again proves that goroutines are cooperatively scheduled. Let’s add another time.Sleep call in-between print operation in function definition to tell scheduler to schedule other available goroutine.

https://play.golang.org/p/lrSIEdNxSaH

In above program, we printed extra information to see when a print statement is executing since the time of execution of the program. In theory, main goroutine will sleep for 200 milli-seconds, hence all other goroutine must do their job in 200 milli-seconds before it wakes up and kill the program. getChars goroutine will print 1 character and sleep for 10 milli-second, passing control to getDigits goroutine which will print a digit and sleeping for 3 milli-seconds passing control to getChars goroutine again when it wakes up. Since getChars goroutine can print and sleep multiple times, at least 2 times while other goroutines are sleeping, we are hoping to see more characters printed in succession than digits.

Below result is taken from running above program in Windows machine.

main execution started at time 0s

H at time 1.0012ms                         <-|
1 at time 1.0012ms                           | almost at the same time
e at time 11.0283ms                        <-|
l at time 21.0289ms                          | ~10ms apart 
l at time 31.0416ms
2 at time 31.0416ms
o at time 42.0336ms
3 at time 61.0461ms                        <-|
4 at time 91.0647ms                          | 
5 at time 121.0888ms                         | ~30ms apart

main execution stopped at time 200.3137ms    | exiting after 200ms

We can see the pattern we talked about. This will be cleared to you once you see the program execution diagram. We will approximate that print command takes 1ms of CPU time, compared on 200ms scale, that’s negligible.

Now we understood how to create goroutine and how to work with them. But using time.Sleep is just a hack to see the result. In production, we don’t know how much time a goroutine is going to take for the execution. Hence we can't just add random sleep call in main function. We want our goroutines to tell when they finished the execution. Also at this point, we don’t know how we can get data back from other goroutines or pass data to them, simply, communicate with them. This is where channels comes in. Let’s talk about them in next lesson.

☛ Anonymous goroutines

If an anonynous function can exist then anonymous goroutine can also exit. Please read Immedietly invoked function from functions lesson to understand this section. Let’s modify our earlier example of printHellogoroutine.

https://play.golang.org/p/KSzsPIuG-Ph

The result is quite obvious as we defined function and executed as goroutine in the same statement.

All goroutines are anonymous as we learned from concurrency lesson as goroutine does not have an identity. But we are calling that in the sense that function from which it was created was anonymous.

本文章首发在 LearnKu.com 网站上。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://medium.com/rungo/anatomy-of-goro...

译文地址:https://learnku.com/golang/t/31082

参与译者:1
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!