We can think of a goroutine, as a lightweight thread. When a new program starts by calling main
it starts in a goroutine, conveniently called the main goroutine. We can invoke a function in a new goroutine by prefixing the function call by the keyword go
.
Let’s start with a simple program that prints a count up with a delay. No concurrency here yet.
1
2
3
4
5
6
7
8
9
10
11
func main() {
printCountUp("A")
printCountUp("B")
}
func printCountUp(prefix string) {
for i := 0; i < 10; i++ {
fmt.Printf("%s-%d ", prefix, i)
time.Sleep(100 * time.Millisecond)
}
}
Running this outputs the expected result: A-0 A-1 A-2 A-3 A-4 A-5 A-6 A-7 A-8 A-9 B-0 B-1 B-2 B-3 B-4 B-5 B-6 B-7 B-8 B-9
Let’s introduce the go
keyword
1
2
go printCountUp("A") // New goroutine
printCountUp("B") // Still in main goroutine
Now we get B-0 A-0 A-1 B-1 A-2 B-2 A-3 B-3 A-4 B-4 A-5 B-5 A-6 B-6 A-7 B-7 A-8 B-8 A-9 B-9
Which is expected and the program would run almost at half the time. Now let’s go a bit further
1
2
go printCountUp("A") // New goroutine
go printCountUp("B") // New goroutine
The program will terminate immediately and we will get no output. The reason being that the parent goroutine, the main goroutine in this case, has terminated and took its children down with it.
There are many reasons why you would, in a real-world program, start multiple goroutines at the same time and wait for them all to finish. For that, we have a WaitGroup
. Let’s look at a better implementation.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func main() {
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
printCountUp("A")
}()
go func() {
defer wg.Done()
printCountUp("B")
}()
wg.Wait()
}
This prints B-0 A-0 A-1 B-1 A-2 B-2 A-3 B-3 A-4 B-4 A-5 B-5 A-6 B-6 A-7 B-7 A-8 B-8 A-9 B-9
as expected. Note that we can call wg.Add
twice with the argument set to 1
each time. It is just the number of goroutines to wait for.
If we would have called wg.Add(1)
only once, we would have finished execution whenever one of the two goroutines finished executing. If we would have used wg.Add(3)
, we would have gotten a deadlock exception fatal error: all goroutines are asleep - deadlock!
.
Comments powered by Disqus.