Skip to content

Commit 6e2801f

Browse files
committed
Tutorial8 - Concurrency(Goroutines)
1 parent 0c75328 commit 6e2801f

File tree

3 files changed

+157
-0
lines changed

3 files changed

+157
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,4 @@ go run tutorial1/main.go
4949
- [x] [Strings, Runes, and UTF-8 Encoding](tutorial5)
5050
- [x] [Structs and Interfaces](tutorial6)
5151
- [x] [Pointers](tutorial7)
52+
- [x] [Concurrency(Goroutines)](tutorial8)

tutorial8/README.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
## Concurrency(Goroutines) in GO
2+
3+
Concurrency is the execution of multiple instruction sequences at the same time. In Go, concurrency is achieved using goroutines. A goroutine is a lightweight thread of execution managed by the Go runtime. You can launch a function as a goroutine by prefixing the function call with the `go` keyword.
4+
5+
```go
6+
go dbCall(i)
7+
```
8+
9+
However, if we run the code like this, it will feel like nothing happened. Our program spawns these tasks in the background, doesn't wait for them to finish, and then exits the program before they are complete. This is where wait groups come in.
10+
11+
## WaitGroup
12+
13+
A WaitGroup is used to wait for a collection of goroutines to finish executing. The main goroutine calls `Add` to set the number of goroutines to wait for. Then each of the goroutines runs and calls `Done` when finished. At the same time, `Wait` can be used to block until all goroutines have finished.
14+
15+
```go
16+
var wg = sync.WaitGroup{}
17+
18+
func main(){
19+
for i:=0; i<len(dbData); i++{
20+
wg.Add(1)
21+
go dbCall(i)
22+
}
23+
wg.Wait()
24+
}
25+
```
26+
27+
## Mutex
28+
29+
A Mutex, or a mutual exclusion lock, is a synchronization primitive that can be used to protect shared data from being concurrently accessed by multiple goroutines. In Go, `sync.Mutex` is used for this purpose.
30+
31+
```go
32+
var m = sync.Mutex{}
33+
34+
func save(result string){
35+
m.Lock()
36+
results = append(results, result)
37+
m.Unlock()
38+
}
39+
```
40+
41+
However, one drawback of a mutex is that it completely locks out other goroutines from accessing a result slice. This is where `sync.RWMutex` comes in.
42+
43+
## RWMutex
44+
45+
`sync.RWMutex` is a reader/writer mutual exclusion lock. The lock can be held by an arbitrary number of readers or a single writer. It has `RLock` and `RUnlock` methods for reading, and `Lock` and `Unlock` methods for writing.
46+
47+
```go
48+
var m = sync.RWMutex{}
49+
50+
func save(result string){
51+
m.Lock()
52+
results = append(results, result)
53+
m.Unlock()
54+
}
55+
56+
func log(){
57+
m.RLock()
58+
fmt.Printf("\nThe current results are: %v", results)
59+
m.RUnlock()
60+
}
61+
```
62+
63+
### Checkout the code
64+
65+
- [main.go](main.go)
66+

tutorial8/main.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"time"
6+
"sync"
7+
)
8+
9+
//var m = sync.Mutex{} // we can use the mutex to control the writing to or slice in a way that makes it safe in a concurrent program
10+
var m = sync.RWMutex{}
11+
var wg = sync.WaitGroup{}
12+
13+
var dbData = []string{"id1", "id2", "id3", "id4", "id5"}
14+
var results = []string{}
15+
16+
func main(){
17+
t0 := time.Now()
18+
for i:=0; i<len(dbData); i++{
19+
wg.Add(1) //whenever we spawn a new go routine need to make sure to add 1 to the counter
20+
go dbCall(i)
21+
}
22+
wg.Wait()
23+
fmt.Printf("\nTotal execution time: %v", time.Since(t0))
24+
fmt.Printf("\nThe results are %v",results)
25+
}
26+
27+
func dbCall(i int){
28+
// Simulate DB call delay
29+
var delay float32 = 2000
30+
time.Sleep(time.Duration(delay)*time.Millisecond)
31+
fmt.Println("\nThe result from the database is:", dbData[i])
32+
// initial code during mutex
33+
// m.Lock()
34+
// results = append(results, dbData[i])
35+
// m.Unlock()
36+
save(dbData[i])
37+
log()
38+
wg.Done()
39+
}
40+
41+
func save(result string){
42+
m.Lock()
43+
results = append(results, result)
44+
m.Unlock()
45+
}
46+
47+
func log(){
48+
m.RLock()
49+
fmt.Printf("\nThe current results are: %v", results)
50+
m.RUnlock()
51+
}
52+
// one drawback of mutex is it completely locks out othe goroutines to accessing a result slice
53+
54+
/*
55+
func main(){
56+
t0 := time.Now()
57+
for i:=0; i<len(dbData); i++{
58+
dbCall(i)
59+
}
60+
fmt.Printf("\nTotal execution time: %v", time.Since(t0))
61+
}
62+
63+
func dbCall(i int){
64+
// Simulate DB call delay
65+
var delay float32 = rand.Float32()*2000
66+
time.Sleep(time.Duration(delay)*time.Millisecond)
67+
fmt.Println("The result from the database is:", dbData[i])
68+
}
69+
70+
this gave output
71+
The result from the database is: id1
72+
The result from the database is: id2
73+
The result from the database is: id3
74+
The result from the database is: id4
75+
The result from the database is: id5
76+
77+
Total execution time: 4.0624966s
78+
79+
Another way to do this good way to let these db calls run concurrently to do this we use
80+
'go' keyword infront of the function you want to run concurrently
81+
82+
go dbCall(i)
83+
84+
if we run code like this it will feel like that nothing happened
85+
so our program spawn these tasks in the background, didn't wait for them to finish
86+
and then exited the program before they were complete
87+
88+
we need a way for our program to wait until all tasks completed and continue on to the rest of the code
89+
this where wait groups comes in, which can be imported through sync package
90+
*/

0 commit comments

Comments
 (0)