Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Synchronisation in Go.pdf

Synchronisation in Go.pdf

With the great power of concurrency comes great responsibility of synchronization. This talk covers how Go achieve unbelievable concurrency and how to implement that.
With Go’s concurrency paradigm, threading became too easy. However, with multiple ways to achieve synchronization comes a dilemma which method to pick and when. I will take you right from which methodology to pick and why to what Gophers around the world think about this.

Yashish Dua

January 12, 2019
Tweet

More Decks by Yashish Dua

Other Decks in Technology

Transcript

  1. 1. Maturity and Community Support 2. Learning Curve and Simplicity

    3. Performance 4. Error Handling 5. Concurrency and Scalability (Here!) Go vs World
  2. Concurrency is about dealing with lots of things at once.

    Parallelism is about doing lots of things at once. Concurrency vs Parallelism
  3. Concurrency is about dealing with lots of things at once.

    Parallelism is about doing lots of things at once. Concurrency is the composition of independently executing processes, while parallelism is the simultaneous execution of (possibly related) computations. Concurrency vs Parallelism
  4. A thread is just a sequence of instructions that can

    be executed independently by a processor. Communication between threads is simpler as they have a shared memory. What are threads?
  5. 1:1 mapping with OS 1 thread size is >= 1

    MB 1000 threads ~ 1 GB Memory What's the problem with threads?
  6. 1:1 mapping with OS 1 thread size is >= 1

    MB 1000 threads ~ 1 GB Memory Threads need to restore a lot of registers - Floating Point registers, Program Counter (PC), Stack Pointer (SP) What's the problem with threads?
  7. Goroutines are functions or methods that run concurrently with other

    functions or methods. Goroutines can be thought of as light weight threads. With 4KB per stack, you can put 0.25 million goroutines in a gigabyte of RAM Goroutines, to the rescue!
  8. First way to achieve concurrency! Channels can be thought as

    pipes using which Goroutines communicate. Channels
  9. func main() { var a chan int if a ==

    nil { fmt.Println("channel a is nil, going to define it") a = make(chan int) fmt.Printf("Type of a is %T", a) } }
  10. func hello(done chan bool) { fmt.Println("Hello world goroutine") done <-

    true } func main() { done := make(chan bool) go hello(done) <-done fmt.Println("main function") }
  11. Unidirectional Channel func sendData(sendch chan<- int) { sendch <- 10

    } func main() { chnl := make(chan int) go sendData(chnl) fmt.Println(<-chnl) }
  12. Closing Channel func producer(chnl chan int) { for i :=

    0; i < 10; i++ { chnl <- i } close(chnl) } func main() { ch := make(chan int) go producer(ch) for v := range ch { fmt.Println("Received ",v)} }
  13. Fixed sized channels Sends to a buffered channel are blocked

    only when the buffer is full. Similarly receives from a buffered channel are blocked only when the buffer is empty. Buffered Channels
  14. Buffered Channel func main() { ch := make(chan string, 2)

    ch <- "naveen" ch <- "paul" fmt.Println(<- ch) fmt.Println(<- ch) }
  15. Length vs Capacity func main() { ch := make(chan string,

    3) ch <- "naveen" ch <- "paul" fmt.Println("capacity is", cap(ch)) fmt.Println("length is", len(ch)) fmt.Println("read value", <-ch) fmt.Println("new length is", len(ch)) }
  16. A WaitGroup is used to wait for a collection of

    Goroutines to finish executing. The control is blocked until all Goroutines finish executing. WorkerPool is one common implementation of Buffered Channel and WaitGroup. Wait Group
  17. Waitgroup Example func main() { no := 3 var wg

    sync.WaitGroup for i := 0; i < no; i++ { wg.Add(1) go process(i, &wg) } wg.Wait() fmt.Println("All go routines finished executing") }
  18. func process(i int, wg *sync.WaitGroup) { fmt.Println("started Goroutine ", i)

    time.Sleep(2 * time.Second) fmt.Printf("Goroutine %d ended\n", i) wg.Done() }
  19. The select statement is used to choose from multiple send/receive

    channel operations. The select statement blocks until one of the send/receive operation is ready Select
  20. func main() { ch := make(chan string) go process(ch) for

    { time.Sleep(1000 * time.Millisecond) select { case v := <-ch: fmt.Println("received value: ", v) return default: fmt.Println("no value received") } } }
  21. When a program runs concurrently, the parts of code which

    modify shared resources should not be accessed by multiple Goroutines at the same time. Critical Section
  22. var x = 0 func increment(wg *sync.WaitGroup) { x =

    x + 1 wg.Done() } func main() { var w sync.WaitGroup for i := 0; i < 1000; i++ { w.Add(1) go increment(&w) } w.Wait() fmt.Println("final value of x", x) }
  23. Second way to achieve concurrency! A Mutex is used to

    provide a locking mechanism to ensure that only one Goroutine is running the critical section of code at any point of time to prevent race condition from happening. Mutex
  24. var x = 0 func increment(wg *sync.WaitGroup, m *sync.Mutex) {

    m.Lock() x = x + 1 m.Unlock() wg.Done() } func main() { var w sync.WaitGroup var m sync.Mutex for i := 0; i < 1000; i++ { w.Add(1) go increment(&w, &m) } w.Wait() fmt.Println("final value of x", x) }
  25. In general use channels when Goroutines need to communicate with

    each other and mutexes when only one Goroutine should access the critical section of code. Mutex vs Channels