0%

GO_Learning_Fundamental_Concurrency_Goroutine

Put GO in front of target function is the only way to tell Go complier “start a new goroutine here”.

Concurrency Definition

The GO language naturally supports multiple concurrency. Concurrency means Having More Than One Thing In Process;

Goroutine Definition

An operation that does not block in GO will run in a separate process called a goroutine. What’s goroutine?

  • Goroutine is the way in Go language to reach Concurrency level for programmer.

Similar concepts: Process, Threads, Coroutines;

  • Process: the basic unit of resource allocation;
  • Threads: the basic unit of program execution, like CPU ;
  • Coroutine: lightweight threads that switch more quickly;

I don’t want to explain the detail difference between similar concepts, that has been finished with others; What we should know is Goroutine is most similar with Coroutine, while they are different with switcher;

  • Coroutine: the switch with other coroutine is operate by operation system level, which would cost more execution resource;
  • Goroutine: the switch with other goroutine is operate by user level, which would cost less execution resource;

Usage Goroutine

In go language, keyword go means a goroutine; just put the keyword go in front of target function; The Go complier will recognize it as goroutine; For example, there is a function which used to check URL status, called WebsiteChecker:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func WebsiteChecker (url string) bool {
response, err := http.Head(url)
if err != nil {
return false
}

if response.StatusCode != http.StatusOK {
return false
}

return true
}

url := "http://www.facebook.com"

// 1.Use WebsiteChecker with no goroutine
// do somthing before
WebsiteChecker(url)
// do somethind after

// 2.Use WebsiteChecker with goroutine
// do somthing before
go WebsiteChecker(url)
// do somethind after

Tips: the main function is (default) a goroutine; It is the parent goroutine of other goroutine in this process; If the parent goroutine is finished (killed or other finished states), the child goroutine will finished immediately; If child go routine is finished, the parent goroutine doesn’t matter;

WebsiteChecker Case

This case is form TDD tutorial. In this part, i just compare the performance after goroutine.

Basic Function Implement : Website Checker:

1
2
3
4
5
6
7
8
9
10
11
func CheckWebsite(url string) bool {
response, err := http.Head(url)
if err != nil {
return false
}
if response.StatusCode != http.StatusOK {
return false
}

return true
}

Use Website Checker with no goroutine:

1
2
3
4
5
6
7
8
9
10
11
type WebsiteChecker func(string) bool

func CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool {
results := make(map[string]bool)

for _, url := range urls {
results[url] = wc(url)
}

return results
}

Use Website Checker with goroutine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type WebsiteChecker func(string) bool

func CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool {
results := make(map[string]bool)

for _, url := range urls {
go func(u string) {
results[u] = wc(u)
}(url)
}

time.Sleep(2 * time.Second)
return results
}

Compare with test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func slowWebsiteChecker(_ string) bool {
time.Sleep(20 * time.Millisecond)
return true
}

func BenchmarkCheckWebsites(b *testing.B) {
urls := make([]string, 100)
for i := 0; i < len(urls); i++ {
urls[i] = "a url"
}

for i := 0; i < b.N; i++ {
CheckWebsites(slowWebsiteChecker, urls)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
go test -bench=.
// before goroutine
goos: linux
goarch: amd64
pkg: github.com/GuardingDog/tdd/concurrency/v1
cpu: Intel(R) Xeon(R) Platinum 8361HC CPU @ 2.60GHz
BenchmarkCheckWebsites-2 1 2013483386 ns/op
PASS
ok github.com/GuardingDog/tdd/concurrency/v1 2.018s

// after goroutine
goos: linux
goarch: amd64
pkg: github.com/GuardingDog/tdd/concurrency/v3
cpu: Intel(R) Xeon(R) Platinum 8361HC CPU @ 2.60GHz
BenchmarkCheckWebsites-2 57 20275547 ns/op
PASS
ok github.com/GuardingDog/tdd/concurrency/v3 1.182s

From above CLI, before goroutine the process cost 2013483386 ns ~ 2 s each loop; after goroutine the process cost 20275547 ns ~ 20 ms ~ 0.02s each loop; A hundred times better performance;