Programowanie w Go - #13 Goroutines czyli wielowątkowość w Go
Nadszedł czas wkroczyć do krainy smoków, czyli do wielowątkowości. Jest to dziedzina, z której Go jest znane, więc nie możemy o niej zapomnieć. Zajmiemy się goroutines - czym są, jak ich używać, jak je w ogóle synchronizować, bo to najczęściej jest największym zmartwieniem. W kursie programowania w Golang nie mogło tego zabraknąć. Na filmie pokazuję najprostsze sposoby synchronizacji goroutines i nie tylko, sposoby ich uruchamiania i nie tylko.
Goroutines i sprawy wielowątkowości w Go
- Goroutines i sprawy wielowątkowości w Go
- Film o goroutines, wielowątkowości i prostych sposobach synchronizacji
- Przykład z filmu: startowanie funkcji za pomocą słowa kluczowego go
- Przykład z filmu: prosta synchronizacja kanałami
- Przykład z filmu: inne podejście do synchronizacji kanałami
- Przykład z filmu: synchronizacja za pomocą sync.WaitGroup
- Przykład z filmu: case dla race detektora
- Przykład z filmu: gotcha z runtime
Film o goroutines, wielowątkowości i prostych sposobach synchronizacji
Przykład z filmu: startowanie funkcji za pomocą słowa kluczowego go
W tym przykładzie zachęcam do eksperymentowania z kolejnością linii 16
i 17
, by sprawdzić
jak to wpływa na flow programu.
1package main
2
3import (
4 "fmt"
5 "time"
6)
7
8func counter(msg string, num int) {
9 for i := 0; i < num; i++ {
10 fmt.Println(msg, ":", i)
11 time.Sleep(1 * time.Second)
12 }
13}
14
15func main() {
16 go counter("Goroutine1", 5)
17 counter("Normal", 2)
18}
Przykład z filmu: prosta synchronizacja kanałami
1package main
2
3import (
4 "fmt"
5 "time"
6)
7
8func counter2(msg string, synchro chan bool) {
9 for i := 0; i <= 2; i++ {
10 fmt.Println(msg, ":", i)
11 time.Sleep(1 * time.Second)
12 }
13 synchro <- true
14}
15
16func counter5(msg string, synchro chan bool) {
17 for i := 0; i <= 5; i++ {
18 fmt.Println(msg, ":", i)
19 time.Sleep(1 * time.Second)
20 }
21 synchro <- true
22}
23
24func main() {
25 synch2 := make(chan bool)
26 synch5 := make(chan bool)
27 go counter2("To2", synch2)
28 go counter5("To5", synch5)
29
30 <-synch2
31 <-synch5
32}
Przykład z filmu: inne podejście do synchronizacji kanałami
1package main
2
3import (
4 "fmt"
5 "time"
6)
7
8func counter(msg string, counter int, synch chan int) {
9 for i := 0; i <= counter; i++ {
10 fmt.Println(msg, "=>", i)
11 time.Sleep(1 * time.Second)
12 }
13 synch <- 1
14}
15
16func main() {
17
18 chSyn := make(chan int)
19 howMany := 0
20
21 for i := 0; i <= 3; i++ {
22 go counter("To", i, chSyn)
23 howMany++
24 }
25
26 num := 0
27 for {
28 num = num + <-chSyn
29 if howMany == num {
30 fmt.Println("Counting finish, closing channel and get out here")
31 close(chSyn)
32 break
33 }
34 fmt.Println("Still working...")
35 }
36
37}
Przykład z filmu: synchronizacja za pomocą sync.WaitGroup
1package main
2
3import (
4 "fmt"
5 "sync"
6 "time"
7)
8
9func counter(msg string, wg *sync.WaitGroup) {
10 for i := 0; i <= 3; i++ {
11 fmt.Println(msg, "=>", i)
12 time.Sleep(1 * time.Second)
13 }
14 wg.Done()
15}
16
17func main() {
18
19 wg := &sync.WaitGroup{}
20
21 for i := 0; i <= 3; i++ {
22 wg.Add(1)
23 go counter("To3", wg)
24 }
25
26 wg.Wait()
27
28}
Przykład z filmu: case dla race detektora
Poza przykładem, komenda, której używamy: go run -race race.go
gdzie race.go
, to oczywiście
plik z poniższym kodem źródłowym.
1package main
2
3import (
4 "fmt"
5 "math/rand"
6 "sync"
7 "time"
8)
9
10// Source: https://medium.com/@val_deleplace/does-the-race-detector-catch-all-data-races-1afed51d57fb
11
12func main() {
13 // Seeding is important for choosing a random counter
14 rand.Seed(time.Now().Unix())
15
16 // 3 counters
17 a := []int{0, 0, 0}
18
19 // 2 tasks will be launched. The main goroutine will
20 // wait for them to complete.
21 var wg sync.WaitGroup
22 wg.Add(2)
23
24 go func() {
25 i := rand.Intn(3)
26 a[i]++
27 wg.Done()
28 }()
29
30 go func() {
31 j := rand.Intn(3)
32 a[j]++
33 wg.Done()
34 }()
35
36 wg.Wait()
37 fmt.Println(a)
38}
Przykład z filmu: gotcha z runtime
1package main
2
3import (
4 "fmt"
5 "sync"
6)
7
8func main() {
9 var wg sync.WaitGroup
10 wg.Add(5)
11 for i := 0; i < 5; i++ {
12 go func() {
13 fmt.Println(i)
14 wg.Done()
15 }()
16 }
17 wg.Wait()
18}