Concurrency in Go (or, Erlang done right)
Programming Model <ul><li>Mostly CSP/π-calaculus (not actually formalized): goroutines+channels </li></ul><ul><li>Go concu...
Goroutines <ul><li>Think  threads  (however no  join  operation) </li></ul><ul><li>go  foo() </li></ul><ul><li>go  logger....
Channels <ul><li>Basically typed bounded blocking FIFO queues. </li></ul><ul><li>// synchronous chan of ints </li></ul><ul...
Channels: First-class citizens <ul><li>You can pass them as function arguments, store in containers, pass via channels, et...
Channels: input/output <ul><li>func foo(c chan int) { </li></ul><ul><li>   c <- 0 </li></ul><ul><li>   <-c </li></ul><ul><...
Channels: closing <ul><li>func producer(c chan *Work) { </li></ul><ul><li>    defer close(c) </li></ul><ul><li>    for { <...
Why no goroutine join? <ul><li>Usually it's required to return some result anyway. </li></ul><ul><li>   c := make(chan int...
Select <ul><li>Select makes a pseudo-random choice which of a set of possible communications will proceed: </li></ul><ul><...
Select: non-blocking send/recv <ul><li>reqChan := make(chan *Request,  100 ) </li></ul><ul><li>httpReq := parse() </li></u...
Select: timeouts <ul><li>select { </li></ul><ul><li>   case c <- foo: </li></ul><ul><li>   case  <-time.After(1e9) : </li>...
Example: Barber Shop <ul><li>var seats = make(chan Customer, 4) </li></ul><ul><li>func barber() { </li></ul><ul><li>   for...
Example: Resource Pooling <ul><li>Q: General pooling functionality Any has in their code somewhere, a good pooling mechani...
Example: Resource Pooling (cont) <ul><li>The solution is just </li></ul><ul><li>pool := make(chan *Resource, 100) </li></u...
Example: Actor-oriented programming <ul><li>type ReadReq struct { </li></ul><ul><li>   key string </li></ul><ul><li>   ack...
Example: Actor-oriented programming <ul><li>c <-  WriteReq {&quot;foo&quot;, &quot;bar&quot;} </li></ul><ul><li>ack := mak...
Example: Thread Pool <ul><li>[This page intentionally left blank] </li></ul>
Why does pure CSP suck? <ul><li>CSP-style memory allocation: </li></ul><ul><li>ack1 := make(chan *byte) </li></ul><ul><li>...
Why does pure CSP suck? <ul><li>Some application code is no different! </li></ul><ul><li>var UidReq = make(chan chan uint6...
Why does pure CSP suck? <ul><li>“ Message passing is easy to implement. But everything gets turned into distributed progra...
Shared Memory to the Rescue! <ul><li>var seq = uint64(0) </li></ul><ul><li>... </li></ul><ul><li>uid :=  atomic.AddUint64(...
Shared Memory Primitives <ul><li>sync.Mutex </li></ul><ul><li>sync.RWMutex </li></ul><ul><li>sync.Cond </li></ul><ul><li>s...
Mutexes <ul><li>sync.Mutex is actually a cooperative binary semaphore - no ownership, no recursion. </li></ul><ul><li>mtx....
General Scheme <ul><li>90% of CSP on higher levels </li></ul><ul><li>+10% of Shared Memory on lower levels </li></ul><ul><...
Race Detector for Go <ul><li>Currently under development in Google MSK (no obligations to deliver at this stage). </li></u...
Scalability <ul><li>The goal of Go is to support scalable fine-grained concurrency. But it is [was] not yet there. </li></...
  <ul><li>Thanks! </li></ul>
Upcoming SlideShare
Loading in …5
×

Concurrency in go

3,041 views

Published on

Concurrency in go
From: https://docs.google.com/present/view?id=df36r82q_5gdq5gzth&pli=1

Published in: Technology
0 Comments
6 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,041
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
70
Comments
0
Likes
6
Embeds 0
No embeds

No notes for slide
  • Like switch with all cases referring to communication.
  • Why no thread pool? 1. Too many examples 2. It&apos;s trivial in Go (chan func() + N goroutines) 3. You don&apos;t really need it: go func()
  • Concurrency in go

    1. 1. Concurrency in Go (or, Erlang done right)
    2. 2. Programming Model <ul><li>Mostly CSP/π-calaculus (not actually formalized): goroutines+channels </li></ul><ul><li>Go concurrency motto: </li></ul><ul><li>&quot;Do not communicate by sharing memory; instead, share memory by communicating&quot; </li></ul><ul><li>+ Full-fledged shared memory </li></ul>
    3. 3. Goroutines <ul><li>Think threads (however no join operation) </li></ul><ul><li>go foo() </li></ul><ul><li>go logger.Printf(&quot;Hello, %s!&quot;, who) </li></ul><ul><li>go func() { </li></ul><ul><li>     logger.Printf(&quot;Hello, %s!&quot;, who) </li></ul><ul><li>    ... }() </li></ul>
    4. 4. Channels <ul><li>Basically typed bounded blocking FIFO queues. </li></ul><ul><li>// synchronous chan of ints </li></ul><ul><li>c := make(chan int) </li></ul><ul><li>// buffered chan of pointers to Request </li></ul><ul><li>c := make(chan *Request, 100)  </li></ul>
    5. 5. Channels: First-class citizens <ul><li>You can pass them as function arguments, store in containers, pass via channels, etc. </li></ul><ul><li>Moreover, channels are not tied to goroutines. Several goroutines can send/recv from a single channel. </li></ul>
    6. 6. Channels: input/output <ul><li>func foo(c chan int) { </li></ul><ul><li>  c <- 0 </li></ul><ul><li>  <-c </li></ul><ul><li>} </li></ul><ul><li>func bar(c <-chan int) { </li></ul><ul><li>  <-c </li></ul><ul><li>} </li></ul><ul><li>func baz(c chan<- int) { </li></ul><ul><li>  c <- 0 </li></ul><ul><li>} </li></ul>
    7. 7. Channels: closing <ul><li>func producer(c chan *Work) { </li></ul><ul><li>    defer close(c) </li></ul><ul><li>    for { </li></ul><ul><li>        work, ok := getWork() </li></ul><ul><li>        if !ok { return } </li></ul><ul><li>        c <- work </li></ul><ul><li>    } </li></ul><ul><li>} </li></ul><ul><li>// consumer </li></ul><ul><li>for msg := range c { </li></ul><ul><li>    process(msg) </li></ul><ul><li>} </li></ul>
    8. 8. Why no goroutine join? <ul><li>Usually it's required to return some result anyway. </li></ul><ul><li>  c := make(chan int, N) </li></ul><ul><li>  // fork </li></ul><ul><li>  for i := 0; i < N; i++ { </li></ul><ul><li>    go func() { </li></ul><ul><li>      result := ... </li></ul><ul><li>      c <- result </li></ul><ul><li>    }() </li></ul><ul><li>  } </li></ul><ul><li>  // join </li></ul><ul><li>  sum := 0 </li></ul><ul><li>  for i := 0; i < N; i++ { </li></ul><ul><li>    sum += <-c </li></ul><ul><li>  } </li></ul>
    9. 9. Select <ul><li>Select makes a pseudo-random choice which of a set of possible communications will proceed: </li></ul><ul><li>select {   case c1 <- foo: </li></ul><ul><li>  case m := <-c2: </li></ul><ul><li>    doSomething(m) </li></ul><ul><li>  case m := <-c3: </li></ul><ul><li>    doSomethingElse(m) </li></ul><ul><li>  default: </li></ul><ul><li>    doDefault() </li></ul><ul><li>} </li></ul>
    10. 10. Select: non-blocking send/recv <ul><li>reqChan := make(chan *Request, 100 ) </li></ul><ul><li>httpReq := parse() </li></ul><ul><li>select { </li></ul><ul><li>  case reqChan <- httpReq: </li></ul><ul><li>  default : </li></ul><ul><li>    reply(httpReq, 503) </li></ul><ul><li>} </li></ul>
    11. 11. Select: timeouts <ul><li>select { </li></ul><ul><li>  case c <- foo: </li></ul><ul><li>  case <-time.After(1e9) : </li></ul><ul><li>} </li></ul>
    12. 12. Example: Barber Shop <ul><li>var seats = make(chan Customer, 4) </li></ul><ul><li>func barber() { </li></ul><ul><li>  for { </li></ul><ul><li>    c := <-seats </li></ul><ul><li>    // shave c </li></ul><ul><li>  } </li></ul><ul><li>} </li></ul><ul><li>go barber() </li></ul><ul><li>func (c Customer) shave() { </li></ul><ul><li>  select { </li></ul><ul><li>  case seats <- c: </li></ul><ul><li>  default: </li></ul><ul><li>  } </li></ul><ul><li>} </li></ul><ul><li>It is that simple! </li></ul>
    13. 13. Example: Resource Pooling <ul><li>Q: General pooling functionality Any has in their code somewhere, a good pooling mechanism for interface type maybe? Or just somthing one could adapt and abstract? I'm sure some db drivers should have this, any ideas? </li></ul><ul><li>Sounds a bit frightening... </li></ul>
    14. 14. Example: Resource Pooling (cont) <ul><li>The solution is just </li></ul><ul><li>pool := make(chan *Resource, 100) </li></ul><ul><li>Put with [nonblocking] send. </li></ul><ul><li>Get with [nonblocking] recv. </li></ul><ul><li>It is that simple! </li></ul><ul><li>Haa, works like a charm. Go  makes things so nice and simple, yet so powerful! Thanks </li></ul>
    15. 15. Example: Actor-oriented programming <ul><li>type ReadReq struct { </li></ul><ul><li>  key string </li></ul><ul><li>  ack chan<- string </li></ul><ul><li>} </li></ul><ul><li>type WriteReq struct { </li></ul><ul><li>  key, val string </li></ul><ul><li>} </li></ul><ul><li>c := make(chan interface{} ) </li></ul><ul><li>go func() { </li></ul><ul><li>  m := make(map[string]string) </li></ul><ul><li>  for { </li></ul><ul><li>    switch r := (<-c).(type) { </li></ul><ul><li>    case ReadReq: </li></ul><ul><li>      r.ack <- m[r.key] </li></ul><ul><li>    case WriteReq: </li></ul><ul><li>      m[r.key] = r.val </li></ul><ul><li>    } </li></ul><ul><li>  } </li></ul><ul><li>}() </li></ul>
    16. 16. Example: Actor-oriented programming <ul><li>c <- WriteReq {&quot;foo&quot;, &quot;bar&quot;} </li></ul><ul><li>ack := make(chan string) </li></ul><ul><li>c <- ReadReq {&quot;foo&quot;, ack} </li></ul><ul><li>fmt.Printf(&quot;Got&quot;, <-ack) </li></ul><ul><li>It is that simple! </li></ul>
    17. 17. Example: Thread Pool <ul><li>[This page intentionally left blank] </li></ul>
    18. 18. Why does pure CSP suck? <ul><li>CSP-style memory allocation: </li></ul><ul><li>ack1 := make(chan *byte) </li></ul><ul><li>Malloc <- AllocReq{10, ack1} </li></ul><ul><li>ack2 := make(chan *byte) </li></ul><ul><li>Malloc <- AllocReq{20, ack2} </li></ul><ul><li>obj1 := <-ack1 </li></ul><ul><li>obj2 := <-ack2 </li></ul><ul><li>WTF??1! </li></ul>
    19. 19. Why does pure CSP suck? <ul><li>Some application code is no different! </li></ul><ul><li>var UidReq = make(chan chan uint64) </li></ul><ul><li>go func() { </li></ul><ul><li>  seq := uint64(0) </li></ul><ul><li>  for { </li></ul><ul><li>    c := <-UidReq </li></ul><ul><li>    c <- seq </li></ul><ul><li>    seq++ </li></ul><ul><li>  } </li></ul><ul><li>}() </li></ul><ul><li>ack := make(chan uint64) </li></ul><ul><li>UidReq <- ack </li></ul><ul><li>uid := <-ack </li></ul>
    20. 20. Why does pure CSP suck? <ul><li>“ Message passing is easy to implement. But everything gets turned into distributed programming then” (c) Joseph Seigh </li></ul><ul><li>- Additional overheads </li></ul><ul><li>- Additional latency </li></ul><ul><li>- Unnecessary complexity (asynchrony, reorderings) </li></ul><ul><li>- Load balancing </li></ul><ul><li>- Overload control </li></ul><ul><li>- Hard to debug </li></ul>
    21. 21. Shared Memory to the Rescue! <ul><li>var seq = uint64(0) </li></ul><ul><li>... </li></ul><ul><li>uid := atomic.AddUint64(&seq, 1) </li></ul><ul><li>Simple, fast, no additional latency, no bottlenecks, no overloads. </li></ul>
    22. 22. Shared Memory Primitives <ul><li>sync.Mutex </li></ul><ul><li>sync.RWMutex </li></ul><ul><li>sync.Cond </li></ul><ul><li>sync.Once </li></ul><ul><li>sync.WaitGroup </li></ul><ul><li>runtime.Semacquire/Semrelease </li></ul><ul><li>atomic.CompareAndSwap/Add/Load </li></ul>
    23. 23. Mutexes <ul><li>sync.Mutex is actually a cooperative binary semaphore - no ownership, no recursion. </li></ul><ul><li>mtx. Lock () </li></ul><ul><li>go func() { </li></ul><ul><li>    ... </li></ul><ul><li>    mtx. Unlock () } </li></ul><ul><li>mtx. Lock () </li></ul><ul><li>func foo() { </li></ul><ul><li>  mtx.Lock() </li></ul><ul><li>  defer mtx.Unlock() </li></ul><ul><li>  ... </li></ul><ul><li>} </li></ul>
    24. 24. General Scheme <ul><li>90% of CSP on higher levels </li></ul><ul><li>+10% of Shared Memory on lower levels </li></ul><ul><li>  </li></ul>
    25. 25. Race Detector for Go <ul><li>Currently under development in Google MSK (no obligations to deliver at this stage). </li></ul><ul><li>The idea is to provide general support for dynamic analysis tools in Go compiler/runtime/libraries. </li></ul><ul><li>And then attach almighty ThreadSanitizer technology to it. </li></ul><ul><li>If/when committed to main branch, user just specifies a single compiler flag to enable it. </li></ul>
    26. 26. Scalability <ul><li>The goal of Go is to support scalable fine-grained concurrency. But it is [was] not yet there. </li></ul><ul><li>Submitted about 50 scalability-related CLs. Rewrote all sync primitives (mutex, semaphore, once, etc), improve scalability of chan/select, memory allocation, stack mgmt, etc. </li></ul><ul><li>&quot;I've just run a real world benchmark provided by someone using mgo with the r59 release, and it took about 5 seconds out of 20, without any changes in the code.&quot; </li></ul><ul><li>Still to improve goroutine scheduler. </li></ul>
    27. 27.   <ul><li>Thanks! </li></ul>

    ×