Goroutines and Channels Introduction

Golang Tech Talks series

6 October 2020

Obed N Munoz

Cloud Software Engineer

Based on:

Presentation code and examples:

2

Goroutines and Channels

Concurrent programming is the expression of a program as a composition of several autonomous activities.

Golang provides 2 styles of concurrent programming:

3

Goroutines

In Go, each concurrently executing activity is called a goroutine.

When a program starts, its only goroutine is the one that calls the main function. It's called the main goroutine.

f()       // call f(); wait for it to return
go f()    // create a new goroutine that calls f(); don't wait
4

Example 1 - spinner (1/2)

package main

import (
	"fmt"
	"time"
)

func fib(x int) int {
	if x < 2 {
		return x
	}
	return fib(x-1) + fib(x-2)
}

func spinner(delay time.Duration) {
	for {
		for _, r := range `-\|/` {
			fmt.Printf("\r%c", r)
			time.Sleep(delay)
		}
	}
}

func main() {
    go spinner(100 * time.Millisecond)
    const n = 45
    fibN := fib(n) // slow
    fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN)
}
func fib(x int) int {
    if x < 2 {
        return x
    }
    return fib(x-1) + fib(x-2)
}
5

Example 1 - spinner (2/2)

func spinner(delay time.Duration) {
    for {
        for _, r := range `-\|/` {
            fmt.Printf("\r%c", r)
            time.Sleep(delay)
        }
    }
}
6

Example 2 - Concurrent Clock Server (1/4)

// Clock1 is a TCP server that periodically writes the time.
package main

import (
	"io"
	"log"
	"net"
	"time"
)

func handleConn(c net.Conn) {
	defer c.Close()
	for {
		_, err := io.WriteString(c, time.Now().Format("15:04:05\n"))
		if err != nil {
			return // e.g., client disconnected
		}
		time.Sleep(1 * time.Second)
	}
}

func main() {
    listener, err := net.Listen("tcp", "localhost:9090")
    if err != nil {
        log.Fatal(err)
    }
    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Print(err) // e.g., connection aborted
            continue
        }
        handleConn(conn) // handle one connection at a time
    }
}
7

Example 2 - Concurrent Clock Server (2/4)

func handleConn(c net.Conn) {
    defer c.Close()
    for {
        _, err := io.WriteString(c, time.Now().Format("15:04:05\n"))
        if err != nil {
            return // e.g., client disconnected
        }
        time.Sleep(1 * time.Second)
    }
}
#!/bin/bash

nc localhost 9090
8

Example 2 - Concurrent Clock Server (3/4)

// Netcat1 is a read-only TCP client.
package main

import (
	"io"
	"log"
	"net"
	"os"
)

func mustCopy(dst io.Writer, src io.Reader) {
	if _, err := io.Copy(dst, src); err != nil {
		log.Fatal(err)
	}
}

func main() {
    conn, err := net.Dial("tcp", "localhost:9090")
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()
    mustCopy(os.Stdout, conn)
}
func mustCopy(dst io.Writer, src io.Reader) {
    if _, err := io.Copy(dst, src); err != nil {
        log.Fatal(err)
    }
}
9

Example 2 - Concurrent Clock Server (4/4)

This is the real concurrent one

// Clock2 is a concurrent TCP server that periodically writes the time.
package main

import (
	"io"
	"log"
	"net"
	"time"
)

func handleConn(c net.Conn) {
	defer c.Close()
	for {
		_, err := io.WriteString(c, time.Now().Format("15:04:05\n"))
		if err != nil {
			return // e.g., client disconnected
		}
		time.Sleep(1 * time.Second)
	}
}

func main() {
    listener, err := net.Listen("tcp", "localhost:9090")
    if err != nil {
        log.Fatal(err)
    }
    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Print(err) // e.g., connection aborted
            continue
        }
        go handleConn(conn) // handle connections concurrently
    }
}
10

Let's philosophize

11

Channels

If goroutines are the activities of a concurrent Go program, channels are the connections between them.

ch := make(chan int) // ch has type 'chan int'

ch <- x  // a send statement

x = <-ch // a receive expression in an assignment statement
<-ch     // a receive statement; result is discarded

close(ch) // To close a channel
12

Unbuffered and Buffered Channels

Channels created with simple make is called as unbuffered channel.

But make could use a second parameter which indicates the channel's capacity. If this capacity is non-zero, make will created a buffered channel.

ch = make(chan int)    // unbuffered channel
ch = make(chan int, 0) // unbuffered channel
ch = make(chan int, 3) // buffered channel with capacity 3
13

Example 3 - Unbuffered Channels

// Netcat2 is a read-only TCP client with channels
package main

import (
	"io"
	"log"
	"net"
	"os"
)

func main() {
    conn, err := net.Dial("tcp", "localhost:9090")
    if err != nil {
        log.Fatal(err)
    }
    done := make(chan int)
    go func() {
        io.Copy(os.Stdout, conn) // NOTE: ignoring errors
        log.Println("done")
        done <- 2 // signal the main goroutine
    }()

    x := 1
    x = <-done // wait for background goroutine to finish
    log.Println("Channel Closed with value: ", x)
    close(done)
}
14

Pipelines

Pipelines are used to connect goroutines together so that the output of one can be the input to another.

15

Example 4 - Pipelines (1/2)

package main

import (
	"fmt"
)

func counter(naturals chan int) {
	for x := 0; x < 100; x++ {
		naturals <- x
	}
	close(naturals)
}

func squarer(naturals, squares chan int) {
	for x := range naturals {
		squares <- x * x
	}
	close(squares)
}

func main() {
    naturals := make(chan int)
    squares := make(chan int)

    // Counter
    go counter(naturals)

    // Squarer
    go squarer(naturals, squares)

    // Printer (in main goroutine)
    for x := range squares {
        fmt.Println(x)
    }
}
16

Example 4 - Pipelines (2/2)

func counter(naturals chan int) {
    for x := 0; x < 100; x++ {
        naturals <- x
    }
    close(naturals)
}
func squarer(naturals, squares chan int) {
    for x := range naturals {
        squares <- x * x
    }
    close(squares)
}
17

Let's code: ClockWall

Follow the link that matches with your current class:

classify.obedmr.com/get-lab/ap-labs/clockwall

classify.obedmr.com/get-lab/dc-labs/clockwall

18

More topics for second part

Take a look in the book

19

Thank you

Obed N Munoz

Cloud Software Engineer

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)