Golang Tutorial
Introduction Variables Constants Data Type Convert Types Operators If..Else Switch..Case For Loops Functions Variadic Functions Deferred Functions Calls Panic and Recover Arrays Slices Maps Struct Interface Goroutines Channels Concurrency Problems Logs Files and Directories Reading and Writing Files Regular Expression Find DNS records Cryptography Gotchas in Golang Import and Export Best Golang Packages Web Application Goroutines and Channels Exercises Reflection in Golang Golang for beginners Strings in Golang HTTP Client Server Examples Context PackageGolang Reference
Basic Programs Advance Programs Data Structure and Algorithms Date and Time Slice Sort, Reverse, Search Functions String Functions Methods and Objects Interface TypeBeego Framework
Beego Setup Beego Database Migration Beego GET POST Beego RoutingGoroutines and Channels Exercises
Launch multiple Goroutines and each goroutine adding values to a Channel
The program starts with 10 Goroutines. We created a ch string channel and writing data to that channel by running 10 goroutines concurrently. The direction of the arrow with respect to the channel specifies whether the data is sent or received. The arrow points towards ch specifies we are writing to channel ch. The arrow points outwards from ch specifies we are reading from channel ch.
Example
package main
import (
"fmt"
"strconv"
)
func main() {
ch := make(chan string)
for i := 0; i < 10; i++ {
go func(i int) {
for j := 0; j < 10; j++ {
ch <- "Goroutine : " + strconv.Itoa(i)
}
}(i)
}
for k := 1; k <= 100; k++ {
fmt.Println(k, <-ch)
}
}
1 Goroutine : 9
2 Goroutine : 9
3 Goroutine : 2
4 Goroutine : 0
5 Goroutine : 1
6 Goroutine : 5
7 Goroutine : 3
8 Goroutine : 4
9 Goroutine : 7
10 Goroutine : 6
....................
Send and Receive values from Channel
The main function has two functions generator and receiver. We create a c int channel and return it from the generator function. The for loop running inside anonymous goroutines writing the values to the channel c.
Example
package main
import (
"fmt"
)
func main() {
c := generator()
receiver(c)
}
func receiver(c <-chan int) {
for v := range c {
fmt.Println(v)
}
}
func generator() <-chan int {
c := make(chan int)
go func() {
for i := 0; i < 10; i++ {
c <- i
}
close(c)
}()
return c
}
0
1
2
3
4
5
6
7
8
9
Read and Write Fibonacci series to Channel
The main function has two unbuffered channels ch and quit. Inside fibonacci function the select statement blocks until one of its cases is ready.
Example
package main
import (
"fmt"
)
func fibonacci(ch chan int, quit chan bool) {
x, y := 0, 1
for {
select {
case ch <- x: // write to channel ch
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
ch := make(chan int)
quit := make(chan bool)
n := 10
go func(n int) {
for i := 0; i < n; i++ {
fmt.Println(<-ch) // read from channel ch
}
quit <- false
}(n)
fibonacci(ch, quit)
}
0
1
1
2
3
5
8
13
21
34
Goroutines Channels order of execution
You are calling the go routine in your code and you can't tell when the routine will end and the value will be passed to the buffered channel. As this code is asynchronous so whenever the routine will finish it will write the data to the channel and will be read on the other side. In the example above you are calling only two go routines so the behavior is certain and same output is generated somehow for most of the cases but when you will increase the go routines the output will not be same and the order will be different unless you make it synchronous.
Example
package main
import "fmt"
func sum(a []int, c chan int) {
sum := 0
for _, v := range a {
sum += v
}
c <- sum
}
func main() {
a := []int{17, 12, 18, 9, 24, 42, 64, 12, 68, 82, 57, 32, 9, 2, 12, 82, 52, 34, 92, 36}
c := make(chan int)
for i := 0; i < len(a); i = i + 5 {
go sum(a[i:i+5], c)
}
output := make([]int, 5)
for i := 0; i < 4; i++ {
output[i] = <-c
}
close(c)
fmt.Println(output)
}
[296 80 268 112 0]
Find Odd and Even numbers
The below program starts two Goroutines. These two Goroutines now run concurrently. We created two unbuffered channels and passed them to goroutines as argument with the values received by the channels.
Example
package main
import (
"fmt"
)
func main() {
var intSlice = []int{91, 42, 23, 14, 15, 76, 87, 28, 19, 95}
chOdd := make(chan int)
chEven := make(chan int)
go odd(chOdd)
go even(chEven)
for _, value := range intSlice {
if value%2 != 0 {
chOdd <- value
} else {
chEven <- value
}
}
}
func odd(ch <-chan int) {
for v := range ch {
fmt.Println("ODD :", v)
}
}
func even(ch <-chan int) {
for v := range ch {
fmt.Println("EVEN:", v)
}
}
ODD : 91
ODD : 23
EVEN: 42
EVEN: 14
EVEN: 76
ODD : 15
ODD : 87
ODD : 19
ODD : 95
Illustration of the Dining Philosophers Problem
Five silent philosophers sit at a round table with bowls of spaghetti. Forks are placed between each pair of adjacent philosophers. Each philosopher must alternately think and eat. However, a philosopher can only eat spaghetti when they have both left and right forks. Each fork can be held by only one philosopher and so a philosopher can use the fork only if it is not being used by another philosopher. After an individual philosopher finishes eating, they need to put down both forks so that the forks become available to others. A philosopher can take the fork on their right or the one on their left as they become available, but cannot start eating before getting both forks. Eating is not limited by the remaining amounts of spaghetti or stomach space; an infinite supply and an infinite demand are assumed. The problem is how to design a discipline of behavior (a concurrent algorithm) such that no philosopher will starve; i.e., each can forever continue to alternate between eating and thinking, assuming that no philosopher can know when others may want to eat or think.
Example
package main
import (
"hash/fnv"
"log"
"math/rand"
"os"
"sync"
"time"
)
// Number of philosophers is simply the length of this list.
var ph = []string{"Mark", "Russell", "Rocky", "Haris", "Root"}
const hunger = 3 // Number of times each philosopher eats
const think = time.Second / 100 // Mean think time
const eat = time.Second / 100 // Mean eat time
var fmt = log.New(os.Stdout, "", 0)
var dining sync.WaitGroup
func diningProblem(phName string, dominantHand, otherHand *sync.Mutex) {
fmt.Println(phName, "Seated")
h := fnv.New64a()
h.Write([]byte(phName))
rg := rand.New(rand.NewSource(int64(h.Sum64())))
rSleep := func(t time.Duration) {
time.Sleep(t/2 + time.Duration(rg.Int63n(int64(t))))
}
for h := hunger; h > 0; h-- {
fmt.Println(phName, "Hungry")
dominantHand.Lock() // pick up forks
otherHand.Lock()
fmt.Println(phName, "Eating")
rSleep(eat)
dominantHand.Unlock() // put down forks
otherHand.Unlock()
fmt.Println(phName, "Thinking")
rSleep(think)
}
fmt.Println(phName, "Satisfied")
dining.Done()
fmt.Println(phName, "Left the table")
}
func main() {
fmt.Println("Table empty")
dining.Add(5)
fork0 := &sync.Mutex{}
forkLeft := fork0
for i := 1; i < len(ph); i++ {
forkRight := &sync.Mutex{}
go diningProblem(ph[i], forkLeft, forkRight)
forkLeft = forkRight
}
go diningProblem(ph[0], fork0, forkLeft)
dining.Wait() // wait for philosphers to finish
fmt.Println("Table empty")
}
Output
Table empty
Mark seated
Mark Hungry
Mark Eating
..................
..................
Haris Thinking
Haris Satisfied
Haris Left the table
Table empty
Illustration of Checkpoint Synchronization
The checkpoint synchronization is a problem of synchronizing multiple tasks. Consider a workshop where several workers assembling details of some mechanism. When each of them completes his work, they put the details together. There is no store, so a worker who finished its part first must wait for others before starting another one. Putting details together is the checkpoint at which tasks synchronize themselves before going their paths apart.
Example
package main
import (
"log"
"math/rand"
"sync"
"time"
)
func worker(part string) {
log.Println(part, "worker begins part")
time.Sleep(time.Duration(rand.Int63n(1e6)))
log.Println(part, "worker completes part")
wg.Done()
}
var (
partList = []string{"A", "B", "C", "D"}
nAssemblies = 3
wg sync.WaitGroup
)
func main() {
rand.Seed(time.Now().UnixNano())
for c := 1; c <= nAssemblies; c++ {
log.Println("begin assembly cycle", c)
wg.Add(len(partList))
for _, part := range partList {
go worker(part)
}
wg.Wait()
log.Println("assemble. cycle", c, "complete")
}
}
Output
2019/07/15 16:10:32 begin assembly cycle 1
2019/07/15 16:10:32 D worker begins part
2019/07/15 16:10:32 A worker begins part
2019/07/15 16:10:32 B worker begins part
........
2019/07/15 16:10:32 D worker completes part
2019/07/15 16:10:32 C worker completes part
2019/07/15 16:10:32 assemble. cycle 3 complete
Illustration of Producer Consumer Problem
The problem describes two processes, the producer and the consumer, who share a common, fixed-size buffer used as a queue. The producer's job is to generate data, put it into the buffer, and start again. At the same time, the consumer is consuming the data (i.e., removing it from the buffer), one piece at a time. The problem is to make sure that the producer won't try to add data into the buffer if it's full and that the consumer won't try to remove data from an empty buffer. The solution for the producer is to either go to sleep or discard data if the buffer is full. The next time the consumer removes an item from the buffer, it notifies the producer, who starts to fill the buffer again. In the same way, the consumer can go to sleep if it finds the buffer empty. The next time the producer puts data into the buffer, it wakes up the sleeping consumer.
Example
package main
import (
"flag"
"fmt"
"log"
"os"
"runtime"
"runtime/pprof"
)
type Consumer struct {
msgs *chan int
}
// NewConsumer creates a Consumer
func NewConsumer(msgs *chan int) *Consumer {
return &Consumer{msgs: msgs}
}
// consume reads the msgs channel
func (c *Consumer) consume() {
fmt.Println("consume: Started")
for {
msg := <-*c.msgs
fmt.Println("consume: Received:", msg)
}
}
// Producer definition
type Producer struct {
msgs *chan int
done *chan bool
}
// NewProducer creates a Producer
func NewProducer(msgs *chan int, done *chan bool) *Producer {
return &Producer{msgs: msgs, done: done}
}
// produce creates and sends the message through msgs channel
func (p *Producer) produce(max int) {
fmt.Println("produce: Started")
for i := 0; i < max; i++ {
fmt.Println("produce: Sending ", i)
*p.msgs <- i
}
*p.done <- true // signal when done
fmt.Println("produce: Done")
}
func main() {
// profile flags
cpuprofile := flag.String("cpuprofile", "", "write cpu profile to `file`")
memprofile := flag.String("memprofile", "", "write memory profile to `file`")
// get the maximum number of messages from flags
max := flag.Int("n", 5, "defines the number of messages")
flag.Parse()
// utilize the max num of cores available
runtime.GOMAXPROCS(runtime.NumCPU())
// CPU Profile
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
}
var msgs = make(chan int) // channel to send messages
var done = make(chan bool) // channel to control when production is done
// Start a goroutine for Produce.produce
go NewProducer(&msgs, &done).produce(*max)
// Start a goroutine for Consumer.consume
go NewConsumer(&msgs).consume()
// Finish the program when the production is done
<-done
// Memory Profile
if *memprofile != "" {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal("could not create memory profile: ", err)
}
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatal("could not write memory profile: ", err)
}
f.Close()
}
}
Output
consume: Started
produce: Started
produce: Sending 0
produce: Sending 1
consume: Received: 0
consume: Received: 1
produce: Sending 2
produce: Sending 3
consume: Received: 2
consume: Received: 3
produce: Sending 4
produce: Done
Illustration of Sleeping Barber Problem
The barber has one barber's chair in a cutting room and a waiting room containing a number of chairs in it. When the barber finishes cutting a customer's hair, he dismisses the customer and goes to the waiting room to see if there are others waiting. If there are, he brings one of them back to the chair and cuts their hair. If there are none, he returns to the chair and sleeps in it. Each customer, when they arrive, looks to see what the barber is doing. If the barber is sleeping, the customer wakes him up and sits in the cutting room chair. If the barber is cutting hair, the customer stays in the waiting room. If there is a free chair in the waiting room, the customer sits in it and waits their turn. If there is no free chair, the customer leaves.
Example
package main
import (
"fmt"
"sync"
"time"
)
const (
sleeping = iota
checking
cutting
)
var stateLog = map[int]string{
0: "Sleeping",
1: "Checking",
2: "Cutting",
}
var wg *sync.WaitGroup // Amount of potentional customers
type Barber struct {
name string
sync.Mutex
state int // Sleeping/Checking/Cutting
customer *Customer
}
type Customer struct {
name string
}
func (c *Customer) String() string {
return fmt.Sprintf("%p", c)[7:]
}
func NewBarber() (b *Barber) {
return &Barber{
name: "Sam",
state: sleeping,
}
}
// Barber goroutine
// Checks for customers
// Sleeps - wait for wakers to wake him up
func barber(b *Barber, wr chan *Customer, wakers chan *Customer) {
for {
b.Lock()
defer b.Unlock()
b.state = checking
b.customer = nil
// checking the waiting room
fmt.Printf("Checking waiting room: %d\n", len(wr))
time.Sleep(time.Millisecond * 100)
select {
case c := <-wr:
HairCut(c, b)
b.Unlock()
default: // Waiting room is empty
fmt.Printf("Sleeping Barber - %s\n", b.customer)
b.state = sleeping
b.customer = nil
b.Unlock()
c := <-wakers
b.Lock()
fmt.Printf("Woken by %s\n", c)
HairCut(c, b)
b.Unlock()
}
}
}
func HairCut(c *Customer, b *Barber) {
b.state = cutting
b.customer = c
b.Unlock()
fmt.Printf("Cutting %s hair\n", c)
time.Sleep(time.Millisecond * 100)
b.Lock()
wg.Done()
b.customer = nil
}
// customer goroutine
// just fizzles out if it's full, otherwise the customer
// is passed along to the channel handling it's haircut etc
func customer(c *Customer, b *Barber, wr chan<- *Customer, wakers chan<- *Customer) {
// arrive
time.Sleep(time.Millisecond * 50)
// Check on barber
b.Lock()
fmt.Printf("Customer %s checks %s barber | room: %d, w %d - customer: %s\n",
c, stateLog[b.state], len(wr), len(wakers), b.customer)
switch b.state {
case sleeping:
select {
case wakers <- c:
default:
select {
case wr <- c:
default:
wg.Done()
}
}
case cutting:
select {
case wr <- c:
default: // Full waiting room, leave shop
wg.Done()
}
case checking:
panic("Customer shouldn't check for the Barber when Barber is Checking the waiting room")
}
b.Unlock()
}
func main() {
b := NewBarber()
b.name = "Rocky"
WaitingRoom := make(chan *Customer, 5) // 5 chairs
Wakers := make(chan *Customer, 1) // Only one waker at a time
go barber(b, WaitingRoom, Wakers)
time.Sleep(time.Millisecond * 100)
wg = new(sync.WaitGroup)
n := 10
wg.Add(10)
// Spawn customers
for i := 0; i < n; i++ {
time.Sleep(time.Millisecond * 50)
c := new(Customer)
go customer(c, b, WaitingRoom, Wakers)
}
wg.Wait()
fmt.Println("No more customers for the day")
}
Output
Checking waiting room: 0
Sleeping Barber -
Customer 120 checks Sleeping barber | room: 0, w 0 - customer:
Woken by 120
..............
..............
Checking waiting room: 0
No more customers for the day
Illustration of Cigarette Smokers Problem
Assume a cigarette requires three ingredients to make and smoke: tobacco, paper, and matches. There are three smokers around a table, each of whom has an infinite supply of one of the three ingredients — one smoker has an infinite supply of tobacco, another has paper, and the third has matches. A fourth party, with an unlimited supply of everything, chooses at random a smoker, and put on the table the supplies needed for a cigarrette. The chosen smoker smokes, and the process should repeat indefinitely.
Example
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
const (
paper = iota
grass
match
)
var smokeMap = map[int]string{
paper: "paper",
grass: "grass",
match: "match",
}
var names = map[int]string{
paper: "Sandy",
grass: "Apple",
match: "Daisy",
}
type Table struct {
paper chan int
grass chan int
match chan int
}
func arbitrate(t *Table, smokers [3]chan int) {
for {
time.Sleep(time.Millisecond * 500)
next := rand.Intn(3)
fmt.Printf("Table chooses %s: %s\n", smokeMap[next], names[next])
switch next {
case paper:
t.grass <- 1
t.match <- 1
case grass:
t.paper <- 1
t.match <- 1
case match:
t.grass <- 1
t.paper <- 1
}
for _, smoker := range smokers {
smoker <- next
}
wg.Add(1)
wg.Wait()
}
}
func smoker(t *Table, name string, smokes int, signal chan int) {
var chosen = -1
for {
chosen = <-signal // blocks
if smokes != chosen {
continue
}
fmt.Printf("Table: %d grass: %d match: %d\n", len(t.paper), len(t.grass), len(t.match))
select {
case <-t.paper:
case <-t.grass:
case <-t.match:
}
fmt.Printf("Table: %d grass: %d match: %d\n", len(t.paper), len(t.grass), len(t.match))
time.Sleep(10 * time.Millisecond)
select {
case <-t.paper:
case <-t.grass:
case <-t.match:
}
fmt.Printf("Table: %d grass: %d match: %d\n", len(t.paper), len(t.grass), len(t.match))
fmt.Printf("%s smokes a cigarette\n", name)
time.Sleep(time.Millisecond * 500)
wg.Done()
time.Sleep(time.Millisecond * 100)
}
}
const LIMIT = 1
var wg *sync.WaitGroup
func main() {
wg = new(sync.WaitGroup)
table := new(Table)
table.match = make(chan int, LIMIT)
table.paper = make(chan int, LIMIT)
table.grass = make(chan int, LIMIT)
var signals [3]chan int
// three smokers
for i := 0; i < 3; i++ {
signal := make(chan int, 1)
signals[i] = signal
go smoker(table, names[i], i, signal)
}
fmt.Printf("%s, %s, %s, sit with \n%s, %s, %s\n\n", names[0], names[1], names[2], smokeMap[0], smokeMap[1], smokeMap[2])
arbitrate(table, signals)
}
Output
Sandy, Apple, Daisy, sit with
paper, grass, match
Table chooses match: Daisy
Table: 1 grass: 1 match: 0
Table: 1 grass: 0 match: 0
Table: 0 grass: 0 match: 0
Daisy smokes a cigarette
Table chooses paper: Sandy
Table: 0 grass: 1 match: 1
Table: 0 grass: 1 match: 0
Table: 0 grass: 0 match: 0
Sandy smokes a cigarette
Table chooses match: Daisy
Table: 1 grass: 1 match: 0
Table: 1 grass: 0 match: 0
Table: 0 grass: 0 match: 0
Daisy smokes a cigarette