Исходники и презентации
This commit is contained in:
16
lessons/sync_primitives/action_during_actions/main.go
Normal file
16
lessons/sync_primitives/action_during_actions/main.go
Normal file
@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
// Need to show solution
|
||||
|
||||
type Data struct {
|
||||
count atomic.Int32
|
||||
}
|
||||
|
||||
func (d *Data) Process() {
|
||||
d.count.Add(1)
|
||||
if d.count.CompareAndSwap(100, 0) {
|
||||
// do something...
|
||||
}
|
||||
}
|
||||
33
lessons/sync_primitives/atomic_performance/perf_test.go
Normal file
33
lessons/sync_primitives/atomic_performance/perf_test.go
Normal file
@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// go test -bench=. perf_test.go
|
||||
|
||||
func BenchmarkMutexAdd(b *testing.B) {
|
||||
var number int32
|
||||
var mutex sync.Mutex
|
||||
for i := 0; i < b.N; i++ {
|
||||
mutex.Lock()
|
||||
number++
|
||||
mutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAtomicAdd(b *testing.B) {
|
||||
var number atomic.Int32
|
||||
for i := 0; i < b.N; i++ {
|
||||
number.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAdd(b *testing.B) {
|
||||
var number int32
|
||||
for i := 0; i < b.N; i++ {
|
||||
number++
|
||||
}
|
||||
}
|
||||
15
lessons/sync_primitives/cas_loop/main.go
Normal file
15
lessons/sync_primitives/cas_loop/main.go
Normal file
@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
func IncrementAndGet(pointer *int32) int32 {
|
||||
for {
|
||||
currentValue := atomic.LoadInt32(pointer)
|
||||
nextValue := currentValue + 1
|
||||
if atomic.CompareAndSwapInt32(pointer, currentValue, nextValue) {
|
||||
return nextValue
|
||||
}
|
||||
}
|
||||
}
|
||||
34
lessons/sync_primitives/compare_and_swap/main.go
Normal file
34
lessons/sync_primitives/compare_and_swap/main.go
Normal file
@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Need to show solution
|
||||
|
||||
var data map[string]string
|
||||
var initialized atomic.Bool
|
||||
|
||||
func initialize() {
|
||||
if !initialized.Load() {
|
||||
initialized.Store(true)
|
||||
data = make(map[string]string)
|
||||
fmt.Println("initialized")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1000)
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
initialize()
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
55
lessons/sync_primitives/cond/main.go
Normal file
55
lessons/sync_primitives/cond/main.go
Normal file
@ -0,0 +1,55 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func subscribe(name string, data map[string]string, c *sync.Cond) {
|
||||
c.L.Lock()
|
||||
|
||||
for len(data) == 0 {
|
||||
c.Wait()
|
||||
}
|
||||
|
||||
log.Printf("[%s] %s\n", name, data["key"])
|
||||
|
||||
c.L.Unlock()
|
||||
}
|
||||
|
||||
func publish(name string, data map[string]string, c *sync.Cond) {
|
||||
time.Sleep(time.Second)
|
||||
|
||||
c.L.Lock()
|
||||
data["key"] = "value"
|
||||
c.L.Unlock()
|
||||
|
||||
log.Printf("[%s] data publisher\n", name)
|
||||
c.Broadcast()
|
||||
}
|
||||
|
||||
func main() {
|
||||
data := map[string]string{}
|
||||
cond := sync.NewCond(&sync.Mutex{})
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(3)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
subscribe("subscriber_1", data, cond)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
subscribe("subscriber_2", data, cond)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
publish("publisher", data, cond)
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
20
lessons/sync_primitives/cond_operations/main.go
Normal file
20
lessons/sync_primitives/cond_operations/main.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import "sync"
|
||||
|
||||
func waitWithoutLock() {
|
||||
cond := sync.NewCond(&sync.Mutex{})
|
||||
cond.Wait()
|
||||
}
|
||||
|
||||
func waitAfterSignal() {
|
||||
cond := sync.NewCond(&sync.Mutex{})
|
||||
cond.Signal()
|
||||
|
||||
cond.L.Lock()
|
||||
cond.Wait()
|
||||
cond.L.Unlock()
|
||||
}
|
||||
|
||||
func main() {
|
||||
}
|
||||
27
lessons/sync_primitives/correct_increment/main.go
Normal file
27
lessons/sync_primitives/correct_increment/main.go
Normal file
@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func main() {
|
||||
mutex := sync.Mutex{}
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1000)
|
||||
|
||||
value := 0
|
||||
for i := 0; i < 1000; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
mutex.Lock()
|
||||
value++
|
||||
mutex.Unlock()
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
fmt.Println(value)
|
||||
}
|
||||
25
lessons/sync_primitives/data_race/main.go
Normal file
25
lessons/sync_primitives/data_race/main.go
Normal file
@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func main() {
|
||||
text := ""
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
text = "hello world"
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
fmt.Println(text)
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
42
lessons/sync_primitives/deadlock/main.go
Normal file
42
lessons/sync_primitives/deadlock/main.go
Normal file
@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import "sync"
|
||||
|
||||
// Need to show solution
|
||||
|
||||
var resource1 int
|
||||
var resource2 int
|
||||
|
||||
func normalizeResources(lhs, rhs *sync.Mutex) {
|
||||
lhs.Lock()
|
||||
rhs.Lock()
|
||||
|
||||
// normalization
|
||||
|
||||
rhs.Unlock()
|
||||
lhs.Unlock()
|
||||
}
|
||||
|
||||
func main() {
|
||||
var mutex1 sync.Mutex
|
||||
var mutex2 sync.Mutex
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1000)
|
||||
|
||||
for i := 0; i < 500; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
normalizeResources(&mutex1, &mutex2)
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < 500; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
normalizeResources(&mutex2, &mutex1)
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
55
lessons/sync_primitives/deadlock_with_work/main.go
Normal file
55
lessons/sync_primitives/deadlock_with_work/main.go
Normal file
@ -0,0 +1,55 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Need to show solution
|
||||
|
||||
var resource1 int
|
||||
var resource2 int
|
||||
|
||||
func normalizeResources(lhs, rhs *sync.Mutex) {
|
||||
lhs.Lock()
|
||||
rhs.Lock()
|
||||
|
||||
// normalization
|
||||
|
||||
rhs.Unlock()
|
||||
lhs.Unlock()
|
||||
}
|
||||
|
||||
func main() {
|
||||
var mutex1 sync.Mutex
|
||||
var mutex2 sync.Mutex
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1000)
|
||||
|
||||
for i := 0; i < 500; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
normalizeResources(&mutex1, &mutex2)
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < 500; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
normalizeResources(&mutex2, &mutex1)
|
||||
}()
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
log.Println("tick")
|
||||
}
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
21
lessons/sync_primitives/defer_with_mutex/main.go
Normal file
21
lessons/sync_primitives/defer_with_mutex/main.go
Normal file
@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import "sync"
|
||||
|
||||
// Need to show solution
|
||||
|
||||
var mutex sync.Mutex
|
||||
|
||||
func operation() {}
|
||||
|
||||
func doSomething() {
|
||||
mutex.Lock()
|
||||
operation()
|
||||
mutex.Unlock()
|
||||
|
||||
// some long operation
|
||||
|
||||
mutex.Lock()
|
||||
operation()
|
||||
mutex.Unlock()
|
||||
}
|
||||
42
lessons/sync_primitives/defer_with_panic/main.go
Normal file
42
lessons/sync_primitives/defer_with_panic/main.go
Normal file
@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var mutex sync.Mutex
|
||||
|
||||
func functionWithPanic() {
|
||||
panic("error")
|
||||
}
|
||||
|
||||
func handle1() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Println("recovered")
|
||||
}
|
||||
}()
|
||||
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
functionWithPanic()
|
||||
}
|
||||
|
||||
func handle2() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Println("recovered")
|
||||
}
|
||||
}()
|
||||
|
||||
mutex.Lock()
|
||||
functionWithPanic()
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
func main() {
|
||||
handle1()
|
||||
handle2()
|
||||
}
|
||||
73
lessons/sync_primitives/false_sharing/perf_test.go
Normal file
73
lessons/sync_primitives/false_sharing/perf_test.go
Normal file
@ -0,0 +1,73 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// go test -bench=. perf_test.go
|
||||
|
||||
type MutexCounter struct {
|
||||
value int32
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func (c *MutexCounter) Increment(int) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
c.value++
|
||||
}
|
||||
|
||||
func (c *MutexCounter) Get() int32 {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
return c.value
|
||||
}
|
||||
|
||||
type AtomicCounter struct {
|
||||
value atomic.Int32
|
||||
}
|
||||
|
||||
func (c *AtomicCounter) Increment(int) {
|
||||
c.value.Add(1)
|
||||
}
|
||||
|
||||
func (c *AtomicCounter) Get() int32 {
|
||||
return c.value.Load()
|
||||
}
|
||||
|
||||
type ShardedAtomicCounter struct {
|
||||
shards [10]AtomicCounter
|
||||
}
|
||||
|
||||
func (c *ShardedAtomicCounter) Increment(idx int) {
|
||||
c.shards[idx].value.Add(1)
|
||||
}
|
||||
|
||||
func (c *ShardedAtomicCounter) Get() int32 {
|
||||
var value int32
|
||||
for idx := 0; idx < 10; idx++ {
|
||||
value += c.shards[idx].Get()
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func BenchmarkAtomicCounter(b *testing.B) {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(runtime.NumCPU())
|
||||
|
||||
counter := MutexCounter{}
|
||||
for i := 0; i < runtime.NumCPU(); i++ {
|
||||
go func(idx int) {
|
||||
defer wg.Done()
|
||||
for j := 0; j < b.N; j++ {
|
||||
counter.Increment(idx)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
26
lessons/sync_primitives/incorrect_buffer_design/main.go
Normal file
26
lessons/sync_primitives/incorrect_buffer_design/main.go
Normal file
@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import "sync"
|
||||
|
||||
type Buffer struct {
|
||||
mtx sync.Mutex
|
||||
data []int
|
||||
}
|
||||
|
||||
func NewBuffer() *Buffer {
|
||||
return &Buffer{}
|
||||
}
|
||||
|
||||
func (b *Buffer) Add(value int) {
|
||||
b.mtx.Lock()
|
||||
defer b.mtx.Unlock()
|
||||
|
||||
b.data = append(b.data, value)
|
||||
}
|
||||
|
||||
func (b *Buffer) Data() []int {
|
||||
b.mtx.Lock()
|
||||
defer b.mtx.Unlock()
|
||||
|
||||
return b.data
|
||||
}
|
||||
23
lessons/sync_primitives/incorrect_increment/main.go
Normal file
23
lessons/sync_primitives/incorrect_increment/main.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func main() {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1000)
|
||||
|
||||
value := 0
|
||||
for i := 0; i < 1000; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
value++
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
fmt.Println(value)
|
||||
}
|
||||
22
lessons/sync_primitives/incorrect_struct_design/main.go
Normal file
22
lessons/sync_primitives/incorrect_struct_design/main.go
Normal file
@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import "sync"
|
||||
|
||||
type Data struct {
|
||||
sync.Mutex
|
||||
values []int
|
||||
}
|
||||
|
||||
func (d *Data) Add(value int) {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
d.values = append(d.values, value)
|
||||
}
|
||||
|
||||
func main() {
|
||||
data := Data{}
|
||||
data.Add(100)
|
||||
|
||||
data.Unlock() // Possible problem!
|
||||
}
|
||||
57
lessons/sync_primitives/livelock/main.go
Normal file
57
lessons/sync_primitives/livelock/main.go
Normal file
@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Need to show solution
|
||||
|
||||
var mutex1 sync.Mutex
|
||||
var mutex2 sync.Mutex
|
||||
|
||||
func goroutine1() {
|
||||
mutex1.Lock()
|
||||
|
||||
runtime.Gosched()
|
||||
for !mutex2.TryLock() {
|
||||
// active waiting
|
||||
}
|
||||
|
||||
mutex2.Unlock()
|
||||
mutex1.Unlock()
|
||||
|
||||
fmt.Println("goroutine1 finished")
|
||||
}
|
||||
|
||||
func goroutine2() {
|
||||
mutex2.Lock()
|
||||
|
||||
runtime.Gosched()
|
||||
for !mutex1.TryLock() {
|
||||
// active waiting
|
||||
}
|
||||
|
||||
mutex1.Unlock()
|
||||
mutex2.Unlock()
|
||||
|
||||
fmt.Println("goroutine2 finished")
|
||||
}
|
||||
|
||||
func main() {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
goroutine1()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
goroutine2()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
34
lessons/sync_primitives/local_mutex/main.go
Normal file
34
lessons/sync_primitives/local_mutex/main.go
Normal file
@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Need to show solution
|
||||
|
||||
var value int
|
||||
|
||||
func inc() {
|
||||
mutex := sync.Mutex{}
|
||||
|
||||
mutex.Lock()
|
||||
value++
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
func main() {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1000)
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
inc()
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
fmt.Println(value)
|
||||
}
|
||||
16
lessons/sync_primitives/lock_granularity/main.go
Normal file
16
lessons/sync_primitives/lock_granularity/main.go
Normal file
@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var mutex sync.Mutex
|
||||
var cache map[string]string
|
||||
|
||||
func doSomething() {
|
||||
mutex.Lock()
|
||||
item := cache["key"]
|
||||
fmt.Println(item)
|
||||
mutex.Unlock()
|
||||
}
|
||||
20
lessons/sync_primitives/lockable_struct/main.go
Normal file
20
lessons/sync_primitives/lockable_struct/main.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import "sync"
|
||||
|
||||
type Lockable[T any] struct {
|
||||
sync.Mutex
|
||||
Data T
|
||||
}
|
||||
|
||||
func main() {
|
||||
var l1 Lockable[int32]
|
||||
l1.Lock()
|
||||
l1.Data = 100
|
||||
l1.Unlock()
|
||||
|
||||
var l2 Lockable[string]
|
||||
l2.Lock()
|
||||
l2.Data = "test"
|
||||
l2.Unlock()
|
||||
}
|
||||
21
lessons/sync_primitives/mutex_different_operations/main.go
Normal file
21
lessons/sync_primitives/mutex_different_operations/main.go
Normal file
@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var mutex sync.Mutex
|
||||
var value string
|
||||
|
||||
func set(v string) {
|
||||
mutex.Lock()
|
||||
value = v
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
func print() {
|
||||
mutex.Lock()
|
||||
fmt.Println(value)
|
||||
mutex.Unlock()
|
||||
}
|
||||
35
lessons/sync_primitives/mutex_operations/main.go
Normal file
35
lessons/sync_primitives/mutex_operations/main.go
Normal file
@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import "sync"
|
||||
|
||||
func lockAnyTimes() {
|
||||
mutex := sync.Mutex{}
|
||||
mutex.Lock()
|
||||
mutex.Lock()
|
||||
}
|
||||
|
||||
func unlockWithoutLock() {
|
||||
mutex := sync.Mutex{}
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
func unlockFromAnotherGoroutine() {
|
||||
mutex := sync.Mutex{}
|
||||
mutex.Lock()
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
mutex.Unlock()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
mutex.Lock()
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
func main() {
|
||||
}
|
||||
34
lessons/sync_primitives/mutex_with_common_values/main.go
Normal file
34
lessons/sync_primitives/mutex_with_common_values/main.go
Normal file
@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import "sync"
|
||||
|
||||
// go run -race main.go
|
||||
|
||||
type Data struct {
|
||||
X int
|
||||
Y int
|
||||
}
|
||||
|
||||
func main() {
|
||||
var data Data
|
||||
values := make([]int, 2)
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
data.X = 5
|
||||
values[0] = 5
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
data.Y = 10
|
||||
values[1] = 10
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
31
lessons/sync_primitives/mutex_with_local_values/main.go
Normal file
31
lessons/sync_primitives/mutex_with_local_values/main.go
Normal file
@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Need to show solution
|
||||
|
||||
func main() {
|
||||
mutex := sync.Mutex{}
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1000)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
value := 0
|
||||
for j := 0; j < 10; j++ {
|
||||
mutex.Lock()
|
||||
value++
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
log.Println(value)
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
43
lessons/sync_primitives/recursive_lock/main.go
Normal file
43
lessons/sync_primitives/recursive_lock/main.go
Normal file
@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Need to show solution
|
||||
|
||||
type Cache struct {
|
||||
mutex sync.Mutex
|
||||
data map[string]string
|
||||
}
|
||||
|
||||
func NewCache() *Cache {
|
||||
return &Cache{
|
||||
data: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) Set(key, value string) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
c.data[key] = value
|
||||
}
|
||||
|
||||
func (c *Cache) Get(key string) string {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
if c.Size() > 0 {
|
||||
return c.data[key]
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *Cache) Size() int {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
return len(c.data)
|
||||
}
|
||||
25
lessons/sync_primitives/rlocker/main.go
Normal file
25
lessons/sync_primitives/rlocker/main.go
Normal file
@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import "sync"
|
||||
|
||||
func withLock(mutex sync.Locker, action func()) {
|
||||
if action == nil {
|
||||
return
|
||||
}
|
||||
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
action()
|
||||
}
|
||||
|
||||
func main() {
|
||||
mutex := sync.RWMutex{}
|
||||
withLock(&mutex, func() {
|
||||
// write lock
|
||||
})
|
||||
|
||||
withLock(mutex.RLocker(), func() {
|
||||
// read lock
|
||||
})
|
||||
}
|
||||
24
lessons/sync_primitives/rw_mutex_operations/main.go
Normal file
24
lessons/sync_primitives/rw_mutex_operations/main.go
Normal file
@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import "sync"
|
||||
|
||||
func RUnlockLockedMutex() {
|
||||
m := sync.RWMutex{}
|
||||
m.Lock()
|
||||
m.RUnlock()
|
||||
}
|
||||
|
||||
func UnlockRLockedMutex() {
|
||||
m := sync.RWMutex{}
|
||||
m.RLock()
|
||||
m.Unlock()
|
||||
}
|
||||
|
||||
func LockRLockedMutex() {
|
||||
m := sync.RWMutex{}
|
||||
m.Lock()
|
||||
m.RLock()
|
||||
}
|
||||
|
||||
func main() {
|
||||
}
|
||||
28
lessons/sync_primitives/rw_mutex_performance/perf_test.go
Normal file
28
lessons/sync_primitives/rw_mutex_performance/perf_test.go
Normal file
@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// go test -bench=. perf_test.go
|
||||
|
||||
func BenchmarkMutexAdd(b *testing.B) {
|
||||
var number int32
|
||||
var mutex sync.Mutex
|
||||
for i := 0; i < b.N; i++ {
|
||||
mutex.Lock()
|
||||
number++
|
||||
mutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRWMutexAdd(b *testing.B) {
|
||||
var number int32
|
||||
var mutex sync.RWMutex
|
||||
for i := 0; i < b.N; i++ {
|
||||
mutex.Lock()
|
||||
number++
|
||||
mutex.Unlock()
|
||||
}
|
||||
}
|
||||
23
lessons/sync_primitives/rw_mutex_with_map/main.go
Normal file
23
lessons/sync_primitives/rw_mutex_with_map/main.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import "sync"
|
||||
|
||||
type Counters struct {
|
||||
mu sync.RWMutex
|
||||
m map[string]int
|
||||
}
|
||||
|
||||
func (c *Counters) Load(key string) (int, bool) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
value, found := c.m[key]
|
||||
return value, found
|
||||
}
|
||||
|
||||
func (c *Counters) Store(key string, value int) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.m[key] = value
|
||||
}
|
||||
38
lessons/sync_primitives/semaphore/main.go
Normal file
38
lessons/sync_primitives/semaphore/main.go
Normal file
@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Semaphore struct {
|
||||
count int
|
||||
max int
|
||||
condition *sync.Cond
|
||||
}
|
||||
|
||||
func NewSemaphore(limit int) *Semaphore {
|
||||
mutex := &sync.Mutex{}
|
||||
return &Semaphore{
|
||||
max: limit,
|
||||
condition: sync.NewCond(mutex),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Semaphore) Acquire() {
|
||||
s.condition.L.Lock()
|
||||
defer s.condition.L.Unlock()
|
||||
|
||||
for s.count >= s.max {
|
||||
s.condition.Wait()
|
||||
}
|
||||
|
||||
s.count++
|
||||
}
|
||||
|
||||
func (s *Semaphore) Release() {
|
||||
s.condition.L.Lock()
|
||||
defer s.condition.L.Unlock()
|
||||
|
||||
s.count--
|
||||
s.condition.Signal()
|
||||
}
|
||||
59
lessons/sync_primitives/starvation/main.go
Normal file
59
lessons/sync_primitives/starvation/main.go
Normal file
@ -0,0 +1,59 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Starvation may be with processor, memory, file descriptors,
|
||||
// connections with database and so on...
|
||||
|
||||
func main() {
|
||||
var wg sync.WaitGroup
|
||||
var mutex sync.Mutex
|
||||
const runtime = 1 * time.Second
|
||||
|
||||
greedyWorker := func() {
|
||||
defer wg.Done()
|
||||
|
||||
var count int
|
||||
for begin := time.Now(); time.Since(begin) <= runtime; {
|
||||
mutex.Lock()
|
||||
time.Sleep(3 * time.Nanosecond)
|
||||
mutex.Unlock()
|
||||
count++
|
||||
}
|
||||
|
||||
fmt.Printf("Greedy worker was able to execute %v work loops\n", count)
|
||||
}
|
||||
|
||||
politeWorker := func() {
|
||||
defer wg.Done()
|
||||
|
||||
var count int
|
||||
for begin := time.Now(); time.Since(begin) <= runtime; {
|
||||
mutex.Lock()
|
||||
time.Sleep(1 * time.Nanosecond)
|
||||
mutex.Unlock()
|
||||
|
||||
mutex.Lock()
|
||||
time.Sleep(1 * time.Nanosecond)
|
||||
mutex.Unlock()
|
||||
|
||||
mutex.Lock()
|
||||
time.Sleep(1 * time.Nanosecond)
|
||||
mutex.Unlock()
|
||||
|
||||
count++
|
||||
}
|
||||
|
||||
fmt.Printf("Polite worker was able to execute %v work loops.\n", count)
|
||||
}
|
||||
|
||||
wg.Add(2)
|
||||
go greedyWorker()
|
||||
go politeWorker()
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
76
lessons/sync_primitives/sync_stack/main.go
Normal file
76
lessons/sync_primitives/sync_stack/main.go
Normal file
@ -0,0 +1,76 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Need to show solution
|
||||
|
||||
type Stack struct {
|
||||
mutex sync.Mutex
|
||||
data []string
|
||||
}
|
||||
|
||||
func NewStack() Stack {
|
||||
return Stack{}
|
||||
}
|
||||
|
||||
func (b Stack) Push(value string) {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
|
||||
b.data = append(b.data, value)
|
||||
}
|
||||
|
||||
func (b Stack) Pop() {
|
||||
if len(b.data) < 0 {
|
||||
panic("pop: stack is empty")
|
||||
}
|
||||
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
|
||||
b.data = b.data[:len(b.data)-1]
|
||||
}
|
||||
|
||||
func (b Stack) Top() string {
|
||||
if len(b.data) < 0 {
|
||||
panic("top: stack is empty")
|
||||
}
|
||||
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
|
||||
return b.data[len(b.data)-1]
|
||||
}
|
||||
|
||||
var stack Stack
|
||||
|
||||
func producer() {
|
||||
for i := 0; i < 1000; i++ {
|
||||
stack.Push("message")
|
||||
}
|
||||
}
|
||||
|
||||
func consumer() {
|
||||
for i := 0; i < 10; i++ {
|
||||
_ = stack.Top()
|
||||
stack.Pop()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
producer()
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(100)
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
consumer()
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
14
lessons/sync_primitives/wait_group_copying/main.go
Normal file
14
lessons/sync_primitives/wait_group_copying/main.go
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "sync"
|
||||
|
||||
func done(wg sync.WaitGroup) {
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
func main() {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
done(wg)
|
||||
wg.Wait()
|
||||
}
|
||||
16
lessons/sync_primitives/wait_group_operations/main.go
Normal file
16
lessons/sync_primitives/wait_group_operations/main.go
Normal file
@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import "sync"
|
||||
|
||||
func makeNegativeCounter() {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(-10)
|
||||
}
|
||||
|
||||
func waitZeroCounter() {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func main() {
|
||||
}
|
||||
13
lessons/sync_primitives/waiting_goroutines/main.go
Normal file
13
lessons/sync_primitives/waiting_goroutines/main.go
Normal file
@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import "log"
|
||||
|
||||
// Need to show solution
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 5; i++ {
|
||||
go func() {
|
||||
log.Println("test")
|
||||
}()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user