SlideShare una empresa de Scribd logo
1 de 50
Descargar para leer sin conexión
GOROUTINESAND
CHANNELSIN
PRACTICE
GUILHERMEGARNIER
We're hiring!https://blog.guilhermegarnier.com
@gpgarnier https://talentos.globo.com
CONCURRENCYIS
HARD
Threads are expensive
Hard to share state
Hard to manage threads lifecycle
WHYISGODIFFERENT?
simple concurrency model
embraces concurrency with
goroutines
easy to share state with
channels
WHAT'SA
GOROUTINE?
Abstraction above OS threads
Don't have an identity like thread IDs
Not suspended unless sleeping, blocked or on a function call
SCHEDULING
Goroutines Threads
Scheduled by Go runtime OS kernel
Work
distribution
de ned in the
code
de ned by the Go
scheduler
GOM:NSCHEDULER
Maps M goroutines to N threads
numGoroutines := 2
wg := sync.WaitGroup{}
wg.Add(numGoroutines)
 
for i := 0; i < numGoroutines; i++ {
go func() {
defer wg.Done()
for i := 0; i < 100; i++ {
for j := 0; j < 10000000; j++ {
}
}
}()
}
 
wg.Wait()
TRACINGTHEEXECUTION
Runnable goroutines
Running goroutines
go run main.go (multithread)
GOMAXPROCS=1 go run main.go (single thread)
numGoroutines := 2
wg := sync.WaitGroup{}
wg.Add(numGoroutines)
 
for i := 0; i < numGoroutines; i++ {
go func() {
defer wg.Done()
for i := 0; i < 100; i++ {
for j := 0; j < 10000000; j++ {
}
time.Sleep(1 * time.Nanosecond)
}
}()
}
 
wg.Wait()
TRACINGTHEEXECUTION
Runnable goroutines
Running goroutines
GOMAXPROCS=1 go run main.go (single thread, with sleep)
STACKSIZE
THREADS
xed (~ 1-8 MB)
wasteful for small jobs
not enough for large jobs
GOROUTINES
dynamic
starts small (~ 4 kB)
grows and shrinks when
needed
GOROUTINESMEMORYUSAGE
source: Concurrency in Go, page 44
memConsumed := func() uint64 {...}
var ch <-chan interface{}
var wg sync.WaitGroup
 
const numGoroutines = 100000
wg.Add(numGoroutines)
 
before := memConsumed()
for i := 0; i < numGoroutines; i++ {
go func() { wg.Done(); <-ch }()
}
wg.Wait()
after := memConsumed()
 
fmt.Printf("%.3f bytes", float64(after-before)/numGoroutines)
GOROUTINESMEMORYUSAGE
Mem (GB) Goroutines
1 370 k
2 740 k
4 1.5 M
8 2.9 M
16 5.9 M
32 11.8 M
64 23.7 M
source: Concurrency in Go, page 44
STATESHARING
Do not communicate by sharing memory; instead,
share memory by communicating
https://blog.golang.org/share-memory-by-communicating
EXAMPLE:HEALTHCHECKSINJAVA
public class Main {
public static void main(String args[]) {
String[] urls = {"url1", "url2", ...};
Thread[] threads = new Thread[urls.length];
for (int i = 0; i < urls.length; i++) {
threads[i] = new Thread(new Healthcheck(urls[i]));
threads[i].start();
}
 
try {
for (Thread t : threads) {
t.join();
}
} catch (Exception e) {}
 
System.out.println(Healthcheck.getOkCount() + " ok, " +
Healthcheck.getErrCount() + " errors");
}
}
EXAMPLE:HEALTHCHECKSINJAVA
class Healthcheck implements Runnable {
private static int okCount = 0;
private static int errCount = 0;
 
public static synchronized void addOk() { Healthcheck.okCount++; }
public static synchronized void addErr() { Healthcheck.errCount++; }
public static int getOkCount() { return Healthcheck.okCount; }
public static int getErrCount() { return Healthcheck.errCount; }
 
public void run() {
try {
if (getStatusCode(this.url) == 200) {
Healthcheck.addOk();
} else {
Healthcheck.addErr();
}
} catch(Exception e) {
Healthcheck.addErr();
}
}
 
private int getStatusCode(String url) throws Exception {
...
}
}
EXAMPLE:HEALTHCHECKSINGO
type Results struct {
okCount int
errCount int
}
 
func main() {
urls := []string{"url1", "url2", ...}
 
r := runChecks(urls)
fmt.Printf("%d ok, %d errorsn", r.okCount, r.errCount)
}
VERSION1:SEQUENTIALCODE
func runChecks(urls []string) Results {
r := Results{}
for _, url := range urls {
resp, err := http.Head(url)
if err != nil || resp.StatusCode != http.StatusOK {
r.errCount++
} else {
r.okCount++
}
}
 
return r
}
VERSION2:USINGGOROUTINES
func runChecks(urls []string) Results {
r := Results{}
lock := sync.Mutex{}
for _, url := range urls {
go func(url string) {
resp, err := http.Head(url)
if err != nil || resp.StatusCode != http.StatusOK {
lock.Lock()
r.errCount++
lock.Unlock()
} else {
lock.Lock()
r.okCount++
lock.Unlock()
}
}(url)
}
 
// Wrong way to wait for goroutines to finish
time.Sleep(2 * time.Second)
 
return r
}
VERSION3:USINGCHANNELS
func runChecks(urls []string) Results {
r := Results{}
responses := make(chan bool, len(urls))
for _, url := range urls {
go func(url string) {
resp, err := http.Head(url)
success := err == nil && resp.StatusCode == http.StatusOK
responses <- success
}(url)
}
 
for i := 0; i < len(urls); i++ {
success := <-responses
if success {
r.okCount++
} else {
r.errCount++
}
}
 
return r
}
WHAT'SA
CHANNEL?
a conduit for streaming information
concurrency-safe
allows decoupling between sender and receiver
UNBUFFEREDCHANNELS
sender and receiver block each other
provide guarantee of delivery
stream := make(chan int)
go func() {
stream <- 1
}()
go func() {
<-stream
}()
BUFFEREDCHANNELS
created with a maximum capacity
sender and receiver block each other when it's full
no guarantee of delivery
stream := make(chan int, 1)
go func() {
stream <- 1
stream <- 2 // blocks until the first receiver
}()
go func() {
<-stream // blocks until the first sender
<-stream
}()
CLOSEDCHANNELS
We can't send values anymore (panic!)
We can still receive values sent before closing it
If empty, receiving values gives the zero value of the channel type
ch := make(chan string, 2)
ch <- "foo"
ch <- "bar"
close(ch)
 
fmt.Println(<-ch) // "foo"
fmt.Println(<-ch) // "bar"
fmt.Println(<-ch) // ""
CLOSEDCHANNELS
How to receive until the channel closes?
If the channel is empty, range blocks until the channel is closed
ch := make(chan string, 2)
 
go func() {
ch <- "foo"
ch <- "bar"
close(ch)
}()
 
for v := range ch {
fmt.Println(v)
}
MULTIPLEXINGCHANNELS
If more than one condition is ready, a case is randomly chosen
select {
case v := <-ch1:
fmt.Println("value read from ch1")
case v := <-ch2:
fmt.Println("value read from ch2")
case ch1 <- 10:
fmt.Println("value sent to ch1")
default:
fmt.Println("channels not ready")
}
UNBLOCKINGRECEIVES
ch := make(chan int)
select {
case v, ok := <-ch:
if ok {
fmt.Printf("Value is %d", v)
} else {
fmt.Print("channel is closed")
}
default:
fmt.Print("channel is empty")
}
TIMINGOUT
ch := make(chan int)
select {
case v := <-ch:
fmt.Printf("Value is %d", v)
case <- time.After(2*time.Second):
fmt.Print("no data after 2 seconds")
}
PIPELINES
PIPELINES
// Inputs an infinite stream
randomNumbers := func() <-chan int {
stream := make(chan int)
go func() {
defer close(stream) // This will never run
for {
stream <- rand.Intn(100)
}
}()
return stream
}
 
// Transforms
double := func(input <-chan int) <-chan int {
stream := make(chan int)
go func() {
defer close(stream)
for i := range input {
stream <- i * 2
}
}()
return stream
}
PIPELINES
// Filters
onlyMultiplesOf10 := func(input <-chan int) <-chan int {
stream := make(chan int)
go func() {
defer close(stream)
for i := range input {
if i%10 == 0 {
stream <- i
}
}
}()
return stream
}
 
pipeline := onlyMultiplesOf10(double(randomNumbers()))
for i := range pipeline { // Infinite loop
fmt.Println(i)
}
MANAGING
GOROUTINES
LIFECYCLE
LIMITINGTHENUMBEROFRUNNINGGOROUTINES
max := 3 // Max simultaneous goroutines
running := make(chan struct{}, max)
 
for url := range urls {
running <- struct{}{} // waits for a free slot
go func(url string) {
defer func() {
<-running // releases slot
}()
// do work
}(url)
}
LIMITINGTHENUMBEROFRUNNINGGOROUTINES
max := 3 // Max simultaneous goroutines
running := make(chan struct{}, max)
 
go func() {
for range time.Tick(100 * time.Millisecond) {
fmt.Printf("%d goroutines runningn", len(running))
}
}()
 
for url := range urls {
running <- struct{}{} // waits for a free slot
go func(url string) {
defer func() {
<-running // releases slot
}()
// do work
}(url)
}
FORCINGGOROUTINESTOSTOP
Using a done channel
done := make(chan struct{})
go func() {
defer func() {
done <- struct{}{}
}()
bufio.NewReader(os.Stdin).ReadByte() // read input from stdin
}()
 
randomNumbers := func() <-chan int {
stream := make(chan int)
go func() {
defer close(stream)
for {
select {
case <-done:
return
default:
stream <- rand.Intn(100)
}
}
}()
return stream
}
FORCINGGOROUTINESTOSTOP
Using context
ctx, cancelFunc := context.WithCancel(context.Background())
go func() {
defer cancelFunc()
bufio.NewReader(os.Stdin).ReadByte() // read input from stdin
}()
 
randomNumbers := func() <-chan int {
stream := make(chan int)
go func() {
defer close(stream)
for {
select {
case <-ctx.Done():
return
default:
stream <- rand.Intn(100)
}
}
}()
return stream
}
FORCINGGOROUTINESTOSTOP
Using context with timeout
ctx, cancelFunc := context.WithTimeout(context.Background(), 10*time.Second)
go func() {
defer cancelFunc()
bufio.NewReader(os.Stdin).ReadByte() // read input from stdin
}()
 
randomNumbers := func() <-chan int {
stream := make(chan int)
go func() {
defer close(stream)
for {
select {
case <-ctx.Done(): // cancelFunc called or timeout reached
return
default:
stream <- rand.Intn(100)
}
}
}()
return stream
}
COMMON
CONCURRENCY
PROBLEMS
DEADLOCK
ch1 := make(chan int)
ch2 := make(chan int)
 
go func() {
n := <-ch2
ch1 <- 2 * n
}()
 
n := <-ch1
ch2 <- 2 * n
DEADLOCK
$ go run main.go
fatal error: all goroutines are asleep - deadlock!
 
goroutine 1 [chan receive]:
main.main()
main.go:12 +0xaa
 
goroutine 17 [chan receive]:
main.main.func1(0xc4200720c0, 0xc420072060)
main.go:8 +0x3e
created by main.main
main.go:7 +0x89
exit status 2
GOROUTINELEAK
urls := make(chan string)
 
go func() {
// defer close(urls)
urls <- "http://example.com/1"
urls <- "http://example.com/2"
}()
 
go func() {
defer fmt.Println("This will never run")
for url := range urls {
http.Get(url)
fmt.Println(url)
}
}()
RACEDETECTORTOOL
func runChecks(urls []string) Results {
r := Results{}
lock := sync.Mutex{}
for _, url := range urls {
go func(url string) {
resp, err := http.Head(url)
if err != nil || resp.StatusCode != http.StatusOK {
lock.Lock()
r.errCount++
lock.Unlock()
} else {
lock.Lock()
r.okCount++
lock.Unlock()
}
}(url)
}
 
// Wrong way to wait for goroutines to finish
time.Sleep(2 * time.Second)
 
return r
}
RACEDETECTORTOOL
$ go build -race
$ ./main
==================
WARNING: DATA RACE
Read at 0x00c4200ee2c0 by main goroutine:
main.runChecks()
main.go:35 +0x16b
main.main()
main.go:45 +0x87
 
Previous write at 0x00c4200ee2c0 by goroutine 7:
main.runChecks.func1()
main.go:26 +0x178
 
Goroutine 7 (finished) created at:
main.runChecks()
main.go:18 +0x123
main.main()
main.go:45 +0x87
==================
==================
WARNING: DATA RACE
...
==================
Found 2 data race(s)
RACEDETECTORTOOL
func runChecks(urls []string) Results {
r := Results{lock: &sync.Mutex{}}
var wg sync.WaitGroup
for _, url := range urls {
wg.Add(1)
go func(url string) {
defer wg.Done()
resp, err := http.Head(url)
if err != nil || resp.StatusCode != http.StatusOK {
r.lock.Lock()
r.errCount++
r.lock.Unlock()
} else {
r.lock.Lock()
r.okCount++
r.lock.Unlock()
}
}(url)
}
 
wg.Wait() // Blocks until all `wg.Done()` calls
return r
}
CONCLUSIONS
Go treats concurrency as rst-class citizen
Goroutines make it easier to handle concurrent code
Channels make it easier to share state and handle goroutines
lifecycle
Concurrency problems are still possible
Do not abuse concurrency
REFERENCES
THANKYOU!
Slides:
https://blog.guilhermegarnier.com
@gpgarnier
https://blog.guilhermegarnier.com/talk-goroutines/

Más contenido relacionado

La actualidad más candente

La actualidad más candente (20)

Golang Channels
Golang ChannelsGolang Channels
Golang Channels
 
GoLang Introduction
GoLang IntroductionGoLang Introduction
GoLang Introduction
 
Go Programming Language (Golang)
Go Programming Language (Golang)Go Programming Language (Golang)
Go Programming Language (Golang)
 
Go Programming Language by Google
Go Programming Language by GoogleGo Programming Language by Google
Go Programming Language by Google
 
Golang 101
Golang 101Golang 101
Golang 101
 
Introduction to go lang
Introduction to go langIntroduction to go lang
Introduction to go lang
 
Go Lang Tutorial
Go Lang TutorialGo Lang Tutorial
Go Lang Tutorial
 
Introduction to Go programming language
Introduction to Go programming languageIntroduction to Go programming language
Introduction to Go programming language
 
Golang - Overview of Go (golang) Language
Golang - Overview of Go (golang) LanguageGolang - Overview of Go (golang) Language
Golang - Overview of Go (golang) Language
 
Go lang
Go langGo lang
Go lang
 
Golang (Go Programming Language)
Golang (Go Programming Language)Golang (Go Programming Language)
Golang (Go Programming Language)
 
Go Programming language, golang
Go Programming language, golangGo Programming language, golang
Go Programming language, golang
 
Golang
GolangGolang
Golang
 
The Go programming language - Intro by MyLittleAdventure
The Go programming language - Intro by MyLittleAdventureThe Go programming language - Intro by MyLittleAdventure
The Go programming language - Intro by MyLittleAdventure
 
Memory in go
Memory in goMemory in go
Memory in go
 
Golang getting started
Golang getting startedGolang getting started
Golang getting started
 
Golang workshop
Golang workshopGolang workshop
Golang workshop
 
Protocol Buffers
Protocol BuffersProtocol Buffers
Protocol Buffers
 
An Actor Model in Go
An Actor Model in GoAn Actor Model in Go
An Actor Model in Go
 
Go language presentation
Go language presentationGo language presentation
Go language presentation
 

Similar a Goroutines and Channels in practice

Степан Кольцов — Rust — лучше, чем C++
Степан Кольцов — Rust — лучше, чем C++Степан Кольцов — Rust — лучше, чем C++
Степан Кольцов — Rust — лучше, чем C++
Yandex
 

Similar a Goroutines and Channels in practice (20)

Job Queue in Golang
Job Queue in GolangJob Queue in Golang
Job Queue in Golang
 
Go ahead, make my day
Go ahead, make my dayGo ahead, make my day
Go ahead, make my day
 
Pdxpugday2010 pg90
Pdxpugday2010 pg90Pdxpugday2010 pg90
Pdxpugday2010 pg90
 
A deep dive into PEP-3156 and the new asyncio module
A deep dive into PEP-3156 and the new asyncio moduleA deep dive into PEP-3156 and the new asyncio module
A deep dive into PEP-3156 and the new asyncio module
 
Implementation of 'go-like' language constructions in scala [english version]
Implementation of 'go-like' language constructions in scala [english version] Implementation of 'go-like' language constructions in scala [english version]
Implementation of 'go-like' language constructions in scala [english version]
 
Writing Docker monitoring agent with Go
Writing Docker monitoring agent with GoWriting Docker monitoring agent with Go
Writing Docker monitoring agent with Go
 
Go vs C++ - CppRussia 2019 Piter BoF
Go vs C++ - CppRussia 2019 Piter BoFGo vs C++ - CppRussia 2019 Piter BoF
Go vs C++ - CppRussia 2019 Piter BoF
 
Programming ppt files (final)
Programming ppt files (final)Programming ppt files (final)
Programming ppt files (final)
 
The Ring programming language version 1.5.4 book - Part 25 of 185
The Ring programming language version 1.5.4 book - Part 25 of 185The Ring programming language version 1.5.4 book - Part 25 of 185
The Ring programming language version 1.5.4 book - Part 25 of 185
 
Go Concurrency Patterns
Go Concurrency PatternsGo Concurrency Patterns
Go Concurrency Patterns
 
Poly-paradigm Java
Poly-paradigm JavaPoly-paradigm Java
Poly-paradigm Java
 
ZeroMQ: Messaging Made Simple
ZeroMQ: Messaging Made SimpleZeroMQ: Messaging Made Simple
ZeroMQ: Messaging Made Simple
 
2 BytesC++ course_2014_c3_ function basics&parameters and overloading
2 BytesC++ course_2014_c3_ function basics&parameters and overloading2 BytesC++ course_2014_c3_ function basics&parameters and overloading
2 BytesC++ course_2014_c3_ function basics&parameters and overloading
 
Scala 2 + 2 > 4
Scala 2 + 2 > 4Scala 2 + 2 > 4
Scala 2 + 2 > 4
 
Степан Кольцов — Rust — лучше, чем C++
Степан Кольцов — Rust — лучше, чем C++Степан Кольцов — Rust — лучше, чем C++
Степан Кольцов — Rust — лучше, чем C++
 
Coroutines in Kotlin. In-depth review
Coroutines in Kotlin. In-depth reviewCoroutines in Kotlin. In-depth review
Coroutines in Kotlin. In-depth review
 
Coroutines in Kotlin. UA Mobile 2017.
Coroutines in Kotlin. UA Mobile 2017.Coroutines in Kotlin. UA Mobile 2017.
Coroutines in Kotlin. UA Mobile 2017.
 
Templates
TemplatesTemplates
Templates
 
Simple API for XML
Simple API for XMLSimple API for XML
Simple API for XML
 
Android taipei 20160225 淺談closure
Android taipei 20160225   淺談closureAndroid taipei 20160225   淺談closure
Android taipei 20160225 淺談closure
 

Último

AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
VictorSzoltysek
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
masabamasaba
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
masabamasaba
 

Último (20)

%in Durban+277-882-255-28 abortion pills for sale in Durban
%in Durban+277-882-255-28 abortion pills for sale in Durban%in Durban+277-882-255-28 abortion pills for sale in Durban
%in Durban+277-882-255-28 abortion pills for sale in Durban
 
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
Chinsurah Escorts ☎️8617697112 Starting From 5K to 15K High Profile Escorts ...
Chinsurah Escorts ☎️8617697112  Starting From 5K to 15K High Profile Escorts ...Chinsurah Escorts ☎️8617697112  Starting From 5K to 15K High Profile Escorts ...
Chinsurah Escorts ☎️8617697112 Starting From 5K to 15K High Profile Escorts ...
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdfPayment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
The Top App Development Trends Shaping the Industry in 2024-25 .pdf
The Top App Development Trends Shaping the Industry in 2024-25 .pdfThe Top App Development Trends Shaping the Industry in 2024-25 .pdf
The Top App Development Trends Shaping the Industry in 2024-25 .pdf
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
Generic or specific? Making sensible software design decisions
Generic or specific? Making sensible software design decisionsGeneric or specific? Making sensible software design decisions
Generic or specific? Making sensible software design decisions
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 

Goroutines and Channels in practice

  • 3.
  • 4.
  • 5. CONCURRENCYIS HARD Threads are expensive Hard to share state Hard to manage threads lifecycle
  • 6. WHYISGODIFFERENT? simple concurrency model embraces concurrency with goroutines easy to share state with channels
  • 7. WHAT'SA GOROUTINE? Abstraction above OS threads Don't have an identity like thread IDs Not suspended unless sleeping, blocked or on a function call
  • 8. SCHEDULING Goroutines Threads Scheduled by Go runtime OS kernel Work distribution de ned in the code de ned by the Go scheduler
  • 10. numGoroutines := 2 wg := sync.WaitGroup{} wg.Add(numGoroutines)   for i := 0; i < numGoroutines; i++ { go func() { defer wg.Done() for i := 0; i < 100; i++ { for j := 0; j < 10000000; j++ { } } }() }   wg.Wait()
  • 11. TRACINGTHEEXECUTION Runnable goroutines Running goroutines go run main.go (multithread) GOMAXPROCS=1 go run main.go (single thread)
  • 12. numGoroutines := 2 wg := sync.WaitGroup{} wg.Add(numGoroutines)   for i := 0; i < numGoroutines; i++ { go func() { defer wg.Done() for i := 0; i < 100; i++ { for j := 0; j < 10000000; j++ { } time.Sleep(1 * time.Nanosecond) } }() }   wg.Wait()
  • 14. STACKSIZE THREADS xed (~ 1-8 MB) wasteful for small jobs not enough for large jobs GOROUTINES dynamic starts small (~ 4 kB) grows and shrinks when needed
  • 15. GOROUTINESMEMORYUSAGE source: Concurrency in Go, page 44 memConsumed := func() uint64 {...} var ch <-chan interface{} var wg sync.WaitGroup   const numGoroutines = 100000 wg.Add(numGoroutines)   before := memConsumed() for i := 0; i < numGoroutines; i++ { go func() { wg.Done(); <-ch }() } wg.Wait() after := memConsumed()   fmt.Printf("%.3f bytes", float64(after-before)/numGoroutines)
  • 16. GOROUTINESMEMORYUSAGE Mem (GB) Goroutines 1 370 k 2 740 k 4 1.5 M 8 2.9 M 16 5.9 M 32 11.8 M 64 23.7 M source: Concurrency in Go, page 44
  • 17. STATESHARING Do not communicate by sharing memory; instead, share memory by communicating https://blog.golang.org/share-memory-by-communicating
  • 18. EXAMPLE:HEALTHCHECKSINJAVA public class Main { public static void main(String args[]) { String[] urls = {"url1", "url2", ...}; Thread[] threads = new Thread[urls.length]; for (int i = 0; i < urls.length; i++) { threads[i] = new Thread(new Healthcheck(urls[i])); threads[i].start(); }   try { for (Thread t : threads) { t.join(); } } catch (Exception e) {}   System.out.println(Healthcheck.getOkCount() + " ok, " + Healthcheck.getErrCount() + " errors"); } }
  • 19. EXAMPLE:HEALTHCHECKSINJAVA class Healthcheck implements Runnable { private static int okCount = 0; private static int errCount = 0;   public static synchronized void addOk() { Healthcheck.okCount++; } public static synchronized void addErr() { Healthcheck.errCount++; } public static int getOkCount() { return Healthcheck.okCount; } public static int getErrCount() { return Healthcheck.errCount; }   public void run() { try { if (getStatusCode(this.url) == 200) { Healthcheck.addOk(); } else { Healthcheck.addErr(); } } catch(Exception e) { Healthcheck.addErr(); } }   private int getStatusCode(String url) throws Exception { ... } }
  • 20. EXAMPLE:HEALTHCHECKSINGO type Results struct { okCount int errCount int }   func main() { urls := []string{"url1", "url2", ...}   r := runChecks(urls) fmt.Printf("%d ok, %d errorsn", r.okCount, r.errCount) }
  • 21. VERSION1:SEQUENTIALCODE func runChecks(urls []string) Results { r := Results{} for _, url := range urls { resp, err := http.Head(url) if err != nil || resp.StatusCode != http.StatusOK { r.errCount++ } else { r.okCount++ } }   return r }
  • 22. VERSION2:USINGGOROUTINES func runChecks(urls []string) Results { r := Results{} lock := sync.Mutex{} for _, url := range urls { go func(url string) { resp, err := http.Head(url) if err != nil || resp.StatusCode != http.StatusOK { lock.Lock() r.errCount++ lock.Unlock() } else { lock.Lock() r.okCount++ lock.Unlock() } }(url) }   // Wrong way to wait for goroutines to finish time.Sleep(2 * time.Second)   return r }
  • 23. VERSION3:USINGCHANNELS func runChecks(urls []string) Results { r := Results{} responses := make(chan bool, len(urls)) for _, url := range urls { go func(url string) { resp, err := http.Head(url) success := err == nil && resp.StatusCode == http.StatusOK responses <- success }(url) }   for i := 0; i < len(urls); i++ { success := <-responses if success { r.okCount++ } else { r.errCount++ } }   return r }
  • 24. WHAT'SA CHANNEL? a conduit for streaming information concurrency-safe allows decoupling between sender and receiver
  • 25. UNBUFFEREDCHANNELS sender and receiver block each other provide guarantee of delivery stream := make(chan int) go func() { stream <- 1 }() go func() { <-stream }()
  • 26. BUFFEREDCHANNELS created with a maximum capacity sender and receiver block each other when it's full no guarantee of delivery stream := make(chan int, 1) go func() { stream <- 1 stream <- 2 // blocks until the first receiver }() go func() { <-stream // blocks until the first sender <-stream }()
  • 27. CLOSEDCHANNELS We can't send values anymore (panic!) We can still receive values sent before closing it If empty, receiving values gives the zero value of the channel type ch := make(chan string, 2) ch <- "foo" ch <- "bar" close(ch)   fmt.Println(<-ch) // "foo" fmt.Println(<-ch) // "bar" fmt.Println(<-ch) // ""
  • 28. CLOSEDCHANNELS How to receive until the channel closes? If the channel is empty, range blocks until the channel is closed ch := make(chan string, 2)   go func() { ch <- "foo" ch <- "bar" close(ch) }()   for v := range ch { fmt.Println(v) }
  • 29. MULTIPLEXINGCHANNELS If more than one condition is ready, a case is randomly chosen select { case v := <-ch1: fmt.Println("value read from ch1") case v := <-ch2: fmt.Println("value read from ch2") case ch1 <- 10: fmt.Println("value sent to ch1") default: fmt.Println("channels not ready") }
  • 30. UNBLOCKINGRECEIVES ch := make(chan int) select { case v, ok := <-ch: if ok { fmt.Printf("Value is %d", v) } else { fmt.Print("channel is closed") } default: fmt.Print("channel is empty") }
  • 31. TIMINGOUT ch := make(chan int) select { case v := <-ch: fmt.Printf("Value is %d", v) case <- time.After(2*time.Second): fmt.Print("no data after 2 seconds") }
  • 33. PIPELINES // Inputs an infinite stream randomNumbers := func() <-chan int { stream := make(chan int) go func() { defer close(stream) // This will never run for { stream <- rand.Intn(100) } }() return stream }   // Transforms double := func(input <-chan int) <-chan int { stream := make(chan int) go func() { defer close(stream) for i := range input { stream <- i * 2 } }() return stream }
  • 34. PIPELINES // Filters onlyMultiplesOf10 := func(input <-chan int) <-chan int { stream := make(chan int) go func() { defer close(stream) for i := range input { if i%10 == 0 { stream <- i } } }() return stream }   pipeline := onlyMultiplesOf10(double(randomNumbers())) for i := range pipeline { // Infinite loop fmt.Println(i) }
  • 36. LIMITINGTHENUMBEROFRUNNINGGOROUTINES max := 3 // Max simultaneous goroutines running := make(chan struct{}, max)   for url := range urls { running <- struct{}{} // waits for a free slot go func(url string) { defer func() { <-running // releases slot }() // do work }(url) }
  • 37. LIMITINGTHENUMBEROFRUNNINGGOROUTINES max := 3 // Max simultaneous goroutines running := make(chan struct{}, max)   go func() { for range time.Tick(100 * time.Millisecond) { fmt.Printf("%d goroutines runningn", len(running)) } }()   for url := range urls { running <- struct{}{} // waits for a free slot go func(url string) { defer func() { <-running // releases slot }() // do work }(url) }
  • 38. FORCINGGOROUTINESTOSTOP Using a done channel done := make(chan struct{}) go func() { defer func() { done <- struct{}{} }() bufio.NewReader(os.Stdin).ReadByte() // read input from stdin }()   randomNumbers := func() <-chan int { stream := make(chan int) go func() { defer close(stream) for { select { case <-done: return default: stream <- rand.Intn(100) } } }() return stream }
  • 39. FORCINGGOROUTINESTOSTOP Using context ctx, cancelFunc := context.WithCancel(context.Background()) go func() { defer cancelFunc() bufio.NewReader(os.Stdin).ReadByte() // read input from stdin }()   randomNumbers := func() <-chan int { stream := make(chan int) go func() { defer close(stream) for { select { case <-ctx.Done(): return default: stream <- rand.Intn(100) } } }() return stream }
  • 40. FORCINGGOROUTINESTOSTOP Using context with timeout ctx, cancelFunc := context.WithTimeout(context.Background(), 10*time.Second) go func() { defer cancelFunc() bufio.NewReader(os.Stdin).ReadByte() // read input from stdin }()   randomNumbers := func() <-chan int { stream := make(chan int) go func() { defer close(stream) for { select { case <-ctx.Done(): // cancelFunc called or timeout reached return default: stream <- rand.Intn(100) } } }() return stream }
  • 42. DEADLOCK ch1 := make(chan int) ch2 := make(chan int)   go func() { n := <-ch2 ch1 <- 2 * n }()   n := <-ch1 ch2 <- 2 * n
  • 43. DEADLOCK $ go run main.go fatal error: all goroutines are asleep - deadlock!   goroutine 1 [chan receive]: main.main() main.go:12 +0xaa   goroutine 17 [chan receive]: main.main.func1(0xc4200720c0, 0xc420072060) main.go:8 +0x3e created by main.main main.go:7 +0x89 exit status 2
  • 44. GOROUTINELEAK urls := make(chan string)   go func() { // defer close(urls) urls <- "http://example.com/1" urls <- "http://example.com/2" }()   go func() { defer fmt.Println("This will never run") for url := range urls { http.Get(url) fmt.Println(url) } }()
  • 45. RACEDETECTORTOOL func runChecks(urls []string) Results { r := Results{} lock := sync.Mutex{} for _, url := range urls { go func(url string) { resp, err := http.Head(url) if err != nil || resp.StatusCode != http.StatusOK { lock.Lock() r.errCount++ lock.Unlock() } else { lock.Lock() r.okCount++ lock.Unlock() } }(url) }   // Wrong way to wait for goroutines to finish time.Sleep(2 * time.Second)   return r }
  • 46. RACEDETECTORTOOL $ go build -race $ ./main ================== WARNING: DATA RACE Read at 0x00c4200ee2c0 by main goroutine: main.runChecks() main.go:35 +0x16b main.main() main.go:45 +0x87   Previous write at 0x00c4200ee2c0 by goroutine 7: main.runChecks.func1() main.go:26 +0x178   Goroutine 7 (finished) created at: main.runChecks() main.go:18 +0x123 main.main() main.go:45 +0x87 ================== ================== WARNING: DATA RACE ... ================== Found 2 data race(s)
  • 47. RACEDETECTORTOOL func runChecks(urls []string) Results { r := Results{lock: &sync.Mutex{}} var wg sync.WaitGroup for _, url := range urls { wg.Add(1) go func(url string) { defer wg.Done() resp, err := http.Head(url) if err != nil || resp.StatusCode != http.StatusOK { r.lock.Lock() r.errCount++ r.lock.Unlock() } else { r.lock.Lock() r.okCount++ r.lock.Unlock() } }(url) }   wg.Wait() // Blocks until all `wg.Done()` calls return r }
  • 48. CONCLUSIONS Go treats concurrency as rst-class citizen Goroutines make it easier to handle concurrent code Channels make it easier to share state and handle goroutines lifecycle Concurrency problems are still possible Do not abuse concurrency