Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
How not to Go wrong with concurrency – Artemiy ...
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
GopherCon Russia
April 13, 2019
Programming
66
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
How not to Go wrong with concurrency – Artemiy Ryabinkov
GopherCon Russia
April 13, 2019
More Decks by GopherCon Russia
See All by GopherCon Russia
Go Profiling from Bottom Up - Felix Geisendörfer
gopherconrussia
0
250
Learning Unsung Gotchas of Go - Rashmi Nagpal
gopherconrussia
1
300
Прозрачный gRPC-proxy один-ко-многим - Андрей Смирнов
gopherconrussia
0
170
Из Python в Go и обратно - Андрей Минкин
gopherconrussia
0
180
Оптимизация работы с PostgreSQL в Go: от 50 до 5000 RPS - Иван Осадчий
gopherconrussia
0
210
Пакет embed: распаковка знаний - Илья Данилкин
gopherconrussia
0
280
За пару мгновений до main() - Олег Ковалев
gopherconrussia
0
160
Тестирование в Go c Ginkgo и Gomega - Александр Егурнов
gopherconrussia
0
150
Building an Autoscaling HTTP Proxy for Kubernetes - Aaron Schlesinger
gopherconrussia
0
160
Other Decks in Programming
See All in Programming
気づいたらRubyで100作品 ー クリエイティブコーディングが生活の一部になるまで / 100 Ruby Sketches Later: How Creative Coding Became Part of My Life
chobishiba
3
550
Inside Stream API
skrb
1
650
Make SRE Operations Easier with Azure SRE Agent
kkamegawa
0
4.3k
Agentic UI
manfredsteyer
PRO
0
110
Oxcを導入して開発体験が向上した話
yug1224
4
290
net-httpのHTTP/2対応について
naruse
0
450
AI 時代のソフトウェア設計の学び方
masuda220
PRO
29
12k
Spec Driven Development | AI Summit Lisbon
danielsogl
PRO
0
160
Javaの型とAI時代に型が大事な理由 / java types and type in AI era
kishida
2
110
AIとASP.NET Coreで雑Webアプリを作った話
mayuki
0
380
Oxlintのカスタムルールの現況
syumai
6
1k
Webフレームワークの ベンチマークについて
yusukebe
0
140
Featured
See All Featured
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
62
54k
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
720
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
570
Believing is Seeing
oripsolob
1
140
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
Code Review Best Practice
trishagee
74
20k
Navigating Weather and Climate Data
rabernat
0
210
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.2k
Utilizing Notion as your number one productivity tool
mfonobong
4
320
So, you think you're a good person
axbom
PRO
2
2.1k
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
480
Transcript
How not to Go wrong with concurrency Artemiy Ryabinkov
None
Go is expressive, concise, clean, and efficient. Its concurrency mechanisms
make it easy to write programs that get the most out of multicore and networked machines. golang.org/doc/
Two Models of Communication Shared Memory Message Passing (CSP and
Actor Model) Locks Mutexes Implicit communication Messages Channels Explicit communication
Do not communicate by sharing memory; instead, share memory by
communicating.
Communicating Sequential Processes
Application Shared Memory Message Passing Mutex RWMutex Wait Chan Chan
+ other ops Lib Docker 9 0 3 5 2 2 Kubernetes 6 2 0 3 6 0 etcd 5 0 0 10 5 1 CockroachDB 4 3 0 5 0 0 gRPC 2 0 0 6 2 1 BoltDB 2 0 0 0 1 0 Total 28 5 3 29 16 4 Blocking Bug Causes https://songlh.github.io/paper/go-study.pdf
https://songlh.github.io/paper/go-study.pdf Our study found that message passing does not necessarily
make multithreaded programs less error-prone than shared memory. In fact, message passing is the main cause of blocking bugs.
Concurrency Parallelism
None
Amdahl’s law
Speedup( P processors ) = Time( 1 processor ) Time(
P processors ) F = inherently sequential fraction of the computation 1+(P-1)F S MAX = P
F = 1% F = 5% F = 10% Max
Speedup Processors 40 32 24 16 8 8 16 24 32 40 48 56 64
Some programs are nicer even if not parallel at all
Synchronization Context Switch Network Request Memory Allocation Disk Read/Write Garbage
Collection
CPU Bound IO Bound Progress is limited by CPU speed
Progress is limited by I/O subsystem speed
Core Cache Line Memory
False Cache-Line Sharing
Core 0 Cache Line Core 1 Cache Line Memory
None
Scheduler
UserSpace Scheduler OS Scheduler CPU
Cooperative multitasking Preemptive multitasking Process makes switch decision Process can
monopolize processor Effective context switch Scheduler makes switch decision Prevents monopolizing Overheads involved with interrupts Fair timeslice
UserSpace Scheduler OS Scheduler CPU Preemptive Preemptive
UserSpace Scheduler OS Scheduler CPU Preemptive Cooperative Gooperative
runtime.GOMAXPROCS(1) x := 0 go func() { for { x++
} }() time.Sleep(500 * time.Millisecond) fmt.Println(x)
runtime.GOMAXPROCS(1) x := 0 go func() { for { runtime.Gosched()
x++ } }() time.Sleep(500 * time.Millisecond) fmt.Println(x)
runtime.morestack() -> runtime.newstack() runtime.Gosched() locks network I/O syscalls Goroutine preemption
points Cyrill Lashkevich - Go Scheduler
Race Conditions
a += 1
a += 1 tmp = a + 1 a =
tmp ⇔
a += 1 if a == 1 { criticalSection() }
a += 1 if a == 1 { criticalSection() }
tmp = a+1 a = tmp if a == 1
{ criticalSection() } tmp = a+1 a = tmp if a == 1 { criticalSection() }
var mx sync.Mutex // .. mx.Lock() tmp = a +
1 a = tmp if a == 1 { criticalSection() } mx.Unlock()
mx.Lock() tmp = a + 1 a = tmp mx.Unlock()
atomic.AddInt64(&a, 1) ⇔
atomic.AddInt64(&a, 1) val = atomic.LoadInt64(&a)
func setup() { a = "hello, world" done = true
} var once sync.Once func doprint() { if !done { once.Do(setup) } print(a) }
func setup() { done = true a = "hello, world"
} var once sync.Once func doprint() { if !done { once.Do(setup) } print(a) }
The Go Memory Model https://golang.org/ref/mem Benign Data Races: What Could
Possibly Go Wrong? https://intel.ly/1MaL4rD
Deadlock
mx1.Lock() mx2.Lock() mx2.Lock() mx1.Lock()
mx1.Lock() mx2.Lock() mx1.Lock() mx2.Lock()
m.Lock() m.Unlock() request <- ch m.Lock() ch <- request m.Unlock()
The Deadlock Empire https://deadlockempire.github.io/
Race Conditions Deadlock/Livelock Starvation False Sharing/Lock Contention Sort of Problems
Race Detector go test -race
Requires test coverage Stores history of N memory access Reports
no false positives. May miss data races Limits of Race Detector
ThreadSanitizerAlgorithm https://bit.ly/2WctkVx Data Race Detector: official docs https://bit.ly/2TXlTEe
Block profile go test -run=XXX -bench=. --blockprofile=block.out go tool pprof
http://.../debug/pprof/block
Let's Code On
func call(ctx context.Context, requests []T) error { for _, req
:= range requests { err := send(ctx, req) if err != nil { return err } } return nil }
func call(ctx context.Context, requests []T) error { errCh := make(chan
error, 1) var wg sync.WaitGroup for _, req := range requests { go func() { wg.Add(1) if err = send(ctx, req); err != nil { errCh <- err } wg.Done() }() } wg.Wait() close(errCh) return <-errCh }
for _, req := range requests { go func() {
wg.Add(1) if err = send(ctx, req); err != nil { errCh <- err } wg.Done() }() } wg.Wait() close(errCh) return <-errCh
for _, req := range requests { go func() {
wg.Add(1) if err = send(ctx, req); err != nil { errCh <- err } wg.Done() }() } wg.Wait() close(errCh) return <-errCh
wg.Add(len(requests)) for _, req := range requests { go func()
{ if err = send(ctx, req); err != nil { errCh <- err } wg.Done() }() } wg.Wait() close(errCh) return <-errCh
wg.Add(len(requests)) for _, req := range requests { go func()
{ if err = send(ctx, req); err != nil { errCh <- err } wg.Done() }() } wg.Wait() close(errCh) return <-errCh
wg.Add(len(requests)) for _, req := range requests { go func(req
T) { if err = send(ctx, req); err != nil { errCh <- err } wg.Done() }(req) } wg.Wait() close(errCh) return <-errCh
errCh := make(chan error, 1) for _, req := range
requests { go func() { if err = send(ctx, req); err != nil { errCh <- err } }() } ... return <-errCh
errCh := make(chan error, 1) for _, req := range
requests { go func() { if err = send(ctx, req); err != nil { errCh <- err } }() } return <-errCh
errCh := make(chan error, len(requests)) for _, req := range
requests { go func() { if err = send(ctx, req); err != nil { errCh <- err } }() } return <-errCh
func call(ctx context.Context, requests []T) error { errCh := make(chan
error, len(requests)) var wg sync.WaitGroup wg.Add(len(requests)) for _, req := range requests { go func(req string) { if err = send(ctx, req); err != nil { errCh <- err } wg.Done() }(req) } wg.Wait() close(errCh) return <-errCh } Dave Cheney: Concurrency made easy
golang.org/x/sync/errgroup
func call(ctx context.Context, requests []T) error { g, ctx :=
errgroup.WithContext(ctx) for _, req := range requests { req := req g.Go(func() error { return send(ctx, req) }) } return g.Wait() }
func call(ctx context.Context, requests []T) error { for _, req
:= range requests { err := send(ctx, req) if err != nil { return err } } return nil }
ctx := context.Background() g, ctx := errgroup.WithContext(ctx) g.Go(func() error {
return DoA(ctx) }) g.Go(func() error { return DoB(ctx) }) err := g.Wait()
As Simple as Possible, but not Simpler Get to know
your abstractions Explicit over implicit syncronization
Go ❤ Concurrency
Artemiy Ryabinkov github.com/furdarius
[email protected]
facebook.com/furdarius