Golang: Undertanding Basic of Concurrence Programming in the easiest way


In this blog post I’m gonna show you an easy example regarding basic concurrency in Golang programming, it will be an easy step-by-step approach to avoid misapprehend to the example (sample).

What is concurrency?

Concurrency is the composition of independently executing computations.

Concurrency is a way to structure software, particularly as a way to write clean code that interacts well with the real world.

It is not parallelism.

Rob Pike, Google

https://talks.golang.org/2012/concurrency.slide#6

Step 1:

package main

import (
	"fmt"
	"time"
)

func sayHello() {
	for x := 0; x < 10; x++ {
		fmt.Println("Hello ", x)
	}
}

func main() {
	start := time.Now()
	fmt.Println("Start at: ", start)
	fmt.Println("------------------------------------------------------")

	go sayHello()

	fmt.Println("------------------------------------------------------")
	fmt.Println("End at: ", time.Now())
	fmt.Println("Done at: ", time.Now().Sub(start))
}

Output:

basic1

In this  step we do not have output to be printed on the screen from sayHello() because the main process ends before the goroutine being executed. Golang program execution begins by initializing the main package and then invoking the function main. When the function main returns, the program exits. It does not wait for other (non-main) goroutines to complete.

A goroutine is a lightweight thread managed by the Go runtime.

Step 2

func main() {
	start := time.Now()
	fmt.Println("Start at: ", start)
	fmt.Println("------------------------------------------------------")

	go sayHello()

	time.Sleep(time.Second)

	fmt.Println("------------------------------------------------------")
	fmt.Println("End at: ", time.Now())
	fmt.Println("Done at: ", time.Now().Sub(start))
}

Output:

Basic2delay

The example above uses delay to make main function (process) waits the goroutine through 1 second before ends, 1 second is more than enough for Golang to print out all the lines on the screen. for the next step I’m gonna add  delay to the loop inside the sayHello() function so it will takes 9 seconds to be finished (and keep 1 second delay in the main function), let’s see what will take place.

Step 3

func sayHello() {
	for x := 0; x < 10; x++ {
		fmt.Println("Hello ", x)
		time.Sleep(time.Second)
	}
}

func main() {
	start := time.Now()
	fmt.Println("Start at: ", start)
	fmt.Println("------------------------------------------------------")

	go sayHello()

	time.Sleep(time.Second)

	fmt.Println("------------------------------------------------------")
	fmt.Println("End at: ", time.Now())
	fmt.Println("Done at: ", time.Now().Sub(start))
}

Output:

Basic3

Each loop waits for 1 second after printed the text, so in 1 minute it adequate to print 2 lines.

Step 4 Make main function wait goroutine to be done before it ends

We can make main function to wait all goroutines to be done before end it’s process without using primitive delay (time.Sleep(time.Second)) function.

package main

import (
	"fmt"
	"time"
	"sync"
)

func sayHello(wg *sync.WaitGroup) {
	for x := 0; x < 10; x++ {
		fmt.Println("Hello ", x)
		time.Sleep(time.Second)

		if x == 8 {
			wg.Done()
		}

	}
}

func main() {
	var wg sync.WaitGroup

	start := time.Now()
	fmt.Println("Start at: ", start)
	fmt.Println("------------------------------------------------------")

	wg.Add(1)
	go sayHello(&wg)

	wg.Wait()

	fmt.Println("------------------------------------------------------")
	fmt.Println("End at: ", time.Now())
	fmt.Println("Done at: ", time.Now().Sub(start))
}

Output:

basic5

Please pay attention to these lines:

var wg sync.WaitGroup
wg.Add(1) // Should be called before goroutine call
wg.Done()
wg.Wait()

Step 5 Go channel

Channels are the pipes that connect concurrent goroutines. You can send values into channels from one goroutine (ch<-) and receive those values into another goroutine (<-ch).

I don’t want to talk much about channel to avoid confusion, instead I will just show you several behavior of channel.

Receive operation <-ch in the main

package main

import (
	"fmt"
)

func sayHello(msg chan string) {

	msg <- "Hello Lorem ipsum1!"
	msg <- "Hello Lorem ipsum2!"
	msg <- "Hello Lorem ipsum3!"
	close(msg)
}

func main() {
	msg := make(chan string)

	go sayHello(msg)

	for value := range(msg) {
		fmt.Println(value);
	}

	// Or you could just uncomment following lines (but comments 'for' lines first)
	// fmt.Println("1:", <-msg)
	// fmt.Println("2:", <-msg)
	// fmt.Println("3:", <-msg)
	// fmt.Println("4:", <-msg) // empty or error if you did't close(msg)
}

Output:

channelx1

package main

import (
      "fmt"
)

func sayHello(msg chan string) {
      says := "Says: "
      for x := 0; x < 10; x++ {
          says += fmt.Sprintf("Hello-%d ,", x)
          fmt.Println("ok")
      }   

      msg <- says
}

func main() {
      msg := make(chan string)
      go sayHello(msg)
      fmt.Println(<-msg)
}

Output:

channel1

Receive operation (<-msg) in main() is a blocking operation, so the program won’t continue until a value is available.

Receive operation <-ch from goroutine to be printed by function

package main

import (
	"fmt"
)

func sayHello(msg chan string) {

	msg <- "hello world"

}

func printHello2(msg string) {
	fmt.Println(msg)

}

func main() {

	msg := make(chan string)

	go sayHello(msg)

	printHello2(<-msg)

}

Output:

exited1

Select

package main

import (
	"fmt"
	"time"
)

func main() {

	// For our example we'll select across two channels.
	c1 := make(chan string)
	c2 := make(chan string)

	go func() {
		time.Sleep(time.Second * 1)
		c1 <- "one"
	}()

	go func() {
		time.Sleep(time.Second * 2)
		c2 <- "two"
	}()

	for {

		select {
		case msg1, ok := <-c1:
			if ok {
				fmt.Println("received", msg1)
			}
		case msg2, ok := <-c2:
			if ok {
				fmt.Println("received", msg2)
			}
		case <-time.After(3000 * time.Millisecond):
			fmt.Printf("Timed out!")
			return
		}

	}
}

Output:

timeout

The select statement lets a goroutine wait on multiple communication operations.

A select blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready.

Advertisements

One thought on “Golang: Undertanding Basic of Concurrence Programming in the easiest way

  1. > Each loop waits for 1 second after printed the text, so in 1 minute it adequate to print 2 lines.
    Should be 1 second 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s