Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 966eea8

Browse files
add more mutexes exercises
1 parent 83e7577 commit 966eea8

File tree

28 files changed

+1093
-32
lines changed

28 files changed

+1093
-32
lines changed

‎mutexes/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,5 +134,6 @@ the database consistency which otherwise is not possible for a non-serial schedu
134134
- [raceacquire - Go Source Code](https://github.com/golang/go/blob/master/src/runtime/race.go#L515)
135135
- [racecall - Go Source Code](https://github.com/golang/go/blob/master/src/runtime/race.go#L348)
136136
- [racecall - GOASM Source Code](https://github.com/golang/go/blob/master/src/runtime/race_amd64.s#L384)
137+
- [RWMutex - Go Source Code](https://github.com/golang/go/blob/master/src/sync/rwmutex.go#L28)
137138

138139
[Home](https://github.com/golang-basics/concurrency)

‎mutexes/atomic-mutex-mix/main.go‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"sync"
6+
"sync/atomic"
7+
)
8+
9+
// try running this with the -race flag
10+
// go run -race main.go
11+
func main() {
12+
var count int32
13+
var mu sync.Mutex
14+
var wg sync.WaitGroup
15+
16+
for i := 0; i < 10000; i++ {
17+
wg.Add(2)
18+
go func() {
19+
defer wg.Done()
20+
atomic.AddInt32(&count, 1)
21+
}()
22+
go func() {
23+
defer wg.Done()
24+
mu.Lock()
25+
count++
26+
mu.Unlock()
27+
}()
28+
}
29+
30+
wg.Wait()
31+
// the result is correct, but Go is not able to properly detect a race condition
32+
// due to mixed usage of Mutex and Atomics
33+
fmt.Println("count:", count)
34+
}

‎mutexes/deadlock-and-race/main.go‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"sync"
6+
)
7+
8+
// Having both deadlock and race
9+
// will result in the program hanging
10+
// when ran with -race flag
11+
// go run -race main.go => will show races, but hang forever
12+
13+
// Try running the program first without -race => fix the deadlock
14+
// go run main.go
15+
// then using the -race flag => fix the race condition
16+
// go run -race main.go
17+
func main() {
18+
var count int
19+
var mu sync.Mutex
20+
var wg sync.WaitGroup
21+
22+
wg.Add(2)
23+
go func() {
24+
defer wg.Done()
25+
count++
26+
27+
}()
28+
go func() {
29+
defer wg.Done()
30+
count++
31+
}()
32+
33+
wg.Wait()
34+
mu.Lock()
35+
mu.Lock()
36+
fmt.Println("count:", count)
37+
}

‎mutexes/exercises/1/exercise.go‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"sync"
6+
"sync/atomic"
7+
)
8+
9+
// EXERCISE:
10+
// Find what's wrong in the following code
11+
// Make sure all the tests are passing
12+
// Make sure there's no deadlock or race condition
13+
// Hint: Limit the go routines (100), then find and fix the deadlock first
14+
15+
// try running this with the -race flag
16+
// go run -race exercise.go
17+
18+
// run the tests using:
19+
// GOFLAGS="-count=1" go test -race .
20+
func main() {
21+
clicks := exercise()
22+
fmt.Println("total clicks:", clicks.total)
23+
}
24+
25+
func exercise() clickCounter {
26+
var mu sync.Mutex
27+
var wg sync.WaitGroup
28+
clicks := clickCounter{
29+
mu: &mu,
30+
elements: map[string]int{
31+
"btn1": 0,
32+
"btn2": 0,
33+
},
34+
}
35+
36+
for i := 0; i < 1000; i++ {
37+
wg.Add(3)
38+
go func() {
39+
defer wg.Done()
40+
if len(clicks.elements) > 10 {
41+
fmt.Println("we got more than 10 elements")
42+
}
43+
}()
44+
go func() {
45+
defer wg.Done()
46+
clicks.add("btn1")
47+
clicks.add("btn2")
48+
}()
49+
go func() {
50+
defer wg.Done()
51+
total := atomic.LoadInt64(&clicks.total)
52+
if total > 20 {
53+
fmt.Println("something is wrong")
54+
}
55+
}()
56+
}
57+
58+
wg.Wait()
59+
return clicks
60+
}
61+
62+
type clickCounter struct {
63+
mu *sync.Mutex
64+
elements map[string]int
65+
total int64
66+
}
67+
68+
func (c *clickCounter) add(element string) {
69+
c.mu.Lock()
70+
if c.elements[element]+1 > 10 {
71+
return
72+
}
73+
c.elements[element]++
74+
c.total++
75+
c.mu.Unlock()
76+
}

‎mutexes/exercises/1/exercise_test.go‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
)
6+
7+
// GOFLAGS="-count=1" go test -race .
8+
func TestExercise(t *testing.T) {
9+
clicks := exercise()
10+
11+
n1 := clicks.elements["btn1"]
12+
n2 := clicks.elements["btn2"]
13+
total := clicks.total
14+
15+
if n1 != 10 {
16+
t.Fatalf("expected number of clicks for btn1: %d, got: %d", 10, n1)
17+
}
18+
if n2 != 10 {
19+
t.Fatalf("expected number of clicks for btn2: %d, got: %d", 10, n2)
20+
}
21+
if total != 20 {
22+
t.Fatalf("expected total to be: %d, got: %d", 20, total)
23+
}
24+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"sync"
6+
)
7+
8+
// EXERCISE:
9+
// Find what's wrong in the following code
10+
// Make sure all the tests are passing
11+
// Make sure there's no deadlock or race condition
12+
// Hint: Limit the go routines (100), then find and fix the deadlock first
13+
14+
// try running this with the -race flag
15+
// go run -race exercise.go
16+
17+
// run the tests using:
18+
// GOFLAGS="-count=1" go test -race .
19+
func main() {
20+
clicks := exercise()
21+
fmt.Println("total clicks:", clicks.total)
22+
}
23+
24+
func exercise() clickCounter {
25+
var mu sync.Mutex
26+
var wg sync.WaitGroup
27+
clicks := clickCounter{
28+
mu: &mu,
29+
elements: map[string]int{
30+
"btn1": 0,
31+
"btn2": 0,
32+
},
33+
}
34+
35+
for i := 0; i < 1000; i++ {
36+
wg.Add(3)
37+
go func() {
38+
defer wg.Done()
39+
clicks.mu.Lock()
40+
defer clicks.mu.Unlock()
41+
if len(clicks.elements) > 10 {
42+
fmt.Println("we got more than 10 elements")
43+
}
44+
}()
45+
go func() {
46+
defer wg.Done()
47+
clicks.add("btn1")
48+
clicks.add("btn2")
49+
}()
50+
go func() {
51+
defer wg.Done()
52+
clicks.mu.Lock()
53+
defer clicks.mu.Unlock()
54+
if clicks.total > 20 {
55+
fmt.Println("something is wrong")
56+
}
57+
}()
58+
}
59+
60+
wg.Wait()
61+
return clicks
62+
}
63+
64+
type clickCounter struct {
65+
mu *sync.Mutex
66+
elements map[string]int
67+
total int64
68+
}
69+
70+
func (c *clickCounter) add(element string) {
71+
c.mu.Lock()
72+
defer c.mu.Unlock()
73+
if c.elements[element]+1 > 10 {
74+
return
75+
}
76+
c.elements[element]++
77+
c.total++
78+
}

‎mutexes/exercises/2/exercise.go‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"sync"
7+
"time"
8+
)
9+
10+
// EXERCISE:
11+
// Find what's wrong in the following code
12+
// Make sure all the tests are passing
13+
// DO NOT remove any Sleep() calls
14+
15+
// try running this with the -race flag
16+
// go run -race exercise.go
17+
18+
// run the tests using:
19+
// GOFLAGS="-count=1" go test .
20+
func main() {
21+
c := &catalog{data: map[string]string{
22+
"p1": "apples",
23+
"p2": "oranges",
24+
"p3": "grapes",
25+
"p4": "pineapple",
26+
"p5": "bananas",
27+
}}
28+
29+
now := time.Now()
30+
exercise(c, "p1", "p2", "p3", "p4", "p5")
31+
fmt.Println("elapsed:", time.Since(now))
32+
}
33+
34+
func exercise(c *catalog, ids ...string) {
35+
var wg sync.WaitGroup
36+
wg.Add(1000)
37+
38+
for i := 0; i < 1000; i++ {
39+
go func(i int) {
40+
defer wg.Done()
41+
for _, id := range ids {
42+
c.get(id)
43+
}
44+
c.add("generated_"+strconv.Itoa(i), "generated product")
45+
}(i+1)
46+
}
47+
48+
wg.Wait()
49+
}
50+
51+
type catalog struct {
52+
mu sync.Mutex
53+
data map[string]string
54+
}
55+
56+
func (c *catalog) add(id, product string) {
57+
c.mu.Lock()
58+
defer c.mu.Unlock()
59+
// simulate load
60+
time.Sleep(500*time.Nanosecond)
61+
c.data[id] = product
62+
}
63+
64+
func (c *catalog) get(id string) string {
65+
c.mu.Lock()
66+
defer c.mu.Unlock()
67+
// simulate load
68+
time.Sleep(500*time.Nanosecond)
69+
// avoid key existence check
70+
return c.data[id]
71+
}

‎mutexes/exercises/2/exercise_test.go‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
"time"
6+
)
7+
8+
// GOFLAGS="-count=1" go test .
9+
func TestExercise(t *testing.T) {
10+
c := &catalog{data: map[string]string{
11+
"p1": "apples",
12+
"p2": "oranges",
13+
"p3": "grapes",
14+
"p4": "pineapple",
15+
"p5": "bananas",
16+
}}
17+
18+
now := time.Now()
19+
exercise(c, "p1", "p2", "p3", "p4", "p5")
20+
elapsed := time.Since(now)
21+
numberOfProducts := len(c.data)
22+
23+
if numberOfProducts != 1005 {
24+
t.Fatalf("wrong number of products in the catalog, got: %d", numberOfProducts)
25+
}
26+
if elapsed > 10*time.Millisecond {
27+
t.Fatalf("exercise took too long, elapsed: %v", elapsed)
28+
}
29+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /